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.

OTFAdvancedTypographicTableReader.java 157KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801
  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.io.IOException;
  20. import java.util.Arrays;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import java.util.Map;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.fop.fonts.truetype.FontFileReader;
  27. import org.apache.fop.fonts.truetype.OFDirTabEntry;
  28. import org.apache.fop.fonts.truetype.OFTableName;
  29. import org.apache.fop.fonts.truetype.OpenFont;
  30. // CSOFF: LineLengthCheck
  31. /**
  32. * <p>OpenType Font (OTF) advanced typographic table reader. Used by @{Link org.apache.fop.fonts.truetype.TTFFile}
  33. * to read advanced typographic tables (GDEF, GSUB, GPOS).</p>
  34. *
  35. * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
  36. */
  37. public final class OTFAdvancedTypographicTableReader {
  38. // logging state
  39. private static Log log = LogFactory.getLog(OTFAdvancedTypographicTableReader.class);
  40. // instance state
  41. private OpenFont otf; // parent font file reader
  42. private FontFileReader in; // input reader
  43. private GlyphDefinitionTable gdef; // glyph definition table
  44. private GlyphSubstitutionTable gsub; // glyph substitution table
  45. private GlyphPositioningTable gpos; // glyph positioning table
  46. // transient parsing state
  47. private transient Map/*<String,Object[3]>*/ seScripts; // script-tag => Object[3] : { default-language-tag, List(language-tag), seLanguages }
  48. private transient Map/*<String,Object[2]>*/ seLanguages; // language-tag => Object[2] : { "f<required-feature-index>", List("f<feature-index>")
  49. private transient Map/*<String,List<String>>*/ seFeatures; // "f<feature-index>" => Object[2] : { feature-tag, List("lu<lookup-index>") }
  50. private transient GlyphMappingTable seMapping; // subtable entry mappings
  51. private transient List seEntries; // subtable entry entries
  52. private transient List seSubtables; // subtable entry subtables
  53. /**
  54. * Construct an <code>OTFAdvancedTypographicTableReader</code> instance.
  55. * @param ttf parent font file reader (must be non-null)
  56. * @param in font file reader (must be non-null)
  57. */
  58. public OTFAdvancedTypographicTableReader(OpenFont otf, FontFileReader in) {
  59. assert otf != null;
  60. assert in != null;
  61. this.otf = otf;
  62. this.in = in;
  63. }
  64. /**
  65. * Read all advanced typographic tables.
  66. * @throws AdvancedTypographicTableFormatException if ATT table has invalid format
  67. */
  68. public void readAll() throws AdvancedTypographicTableFormatException {
  69. try {
  70. readGDEF();
  71. readGSUB();
  72. readGPOS();
  73. } catch (AdvancedTypographicTableFormatException e) {
  74. resetATStateAll();
  75. throw e;
  76. } catch (IOException e) {
  77. resetATStateAll();
  78. throw new AdvancedTypographicTableFormatException(e.getMessage(), e);
  79. } finally {
  80. resetATState();
  81. }
  82. }
  83. /**
  84. * Determine if advanced (typographic) table is present.
  85. * @return true if advanced (typographic) table is present
  86. */
  87. public boolean hasAdvancedTable() {
  88. return (gdef != null) || (gsub != null) || (gpos != null);
  89. }
  90. /**
  91. * Returns the GDEF table or null if none present.
  92. * @return the GDEF table
  93. */
  94. public GlyphDefinitionTable getGDEF() {
  95. return gdef;
  96. }
  97. /**
  98. * Returns the GSUB table or null if none present.
  99. * @return the GSUB table
  100. */
  101. public GlyphSubstitutionTable getGSUB() {
  102. return gsub;
  103. }
  104. /**
  105. * Returns the GPOS table or null if none present.
  106. * @return the GPOS table
  107. */
  108. public GlyphPositioningTable getGPOS() {
  109. return gpos;
  110. }
  111. private void readLangSysTable(OFTableName tableTag, long langSysTable, String langSysTag)
  112. throws IOException {
  113. in.seekSet(langSysTable);
  114. if (log.isDebugEnabled()) {
  115. log.debug(tableTag + " lang sys table: " + langSysTag);
  116. }
  117. // read lookup order (reorder) table offset
  118. int lo = in.readTTFUShort();
  119. // read required feature index
  120. int rf = in.readTTFUShort();
  121. String rfi;
  122. if (rf != 65535) {
  123. rfi = "f" + rf;
  124. } else {
  125. rfi = null;
  126. }
  127. // read (non-required) feature count
  128. int nf = in.readTTFUShort();
  129. // dump info if debugging
  130. if (log.isDebugEnabled()) {
  131. log.debug(tableTag + " lang sys table reorder table: " + lo);
  132. log.debug(tableTag + " lang sys table required feature index: " + rf);
  133. log.debug(tableTag + " lang sys table non-required feature count: " + nf);
  134. }
  135. // read (non-required) feature indices
  136. int[] fia = new int[nf];
  137. List fl = new java.util.ArrayList();
  138. for (int i = 0; i < nf; i++) {
  139. int fi = in.readTTFUShort();
  140. if (log.isDebugEnabled()) {
  141. log.debug(tableTag + " lang sys table non-required feature index: " + fi);
  142. }
  143. fia[i] = fi;
  144. fl.add("f" + fi);
  145. }
  146. if (seLanguages == null) {
  147. seLanguages = new java.util.LinkedHashMap();
  148. }
  149. seLanguages.put(langSysTag, new Object[] { rfi, fl });
  150. }
  151. private static String defaultTag = "dflt";
  152. private void readScriptTable(OFTableName tableTag, long scriptTable, String scriptTag) throws IOException {
  153. in.seekSet(scriptTable);
  154. if (log.isDebugEnabled()) {
  155. log.debug(tableTag + " script table: " + scriptTag);
  156. }
  157. // read default language system table offset
  158. int dl = in.readTTFUShort();
  159. String dt = defaultTag;
  160. if (dl > 0) {
  161. if (log.isDebugEnabled()) {
  162. log.debug(tableTag + " default lang sys tag: " + dt);
  163. log.debug(tableTag + " default lang sys table offset: " + dl);
  164. }
  165. }
  166. // read language system record count
  167. int nl = in.readTTFUShort();
  168. List ll = new java.util.ArrayList();
  169. if (nl > 0) {
  170. String[] lta = new String[nl];
  171. int[] loa = new int[nl];
  172. // read language system records
  173. for (int i = 0, n = nl; i < n; i++) {
  174. String lt = in.readTTFString(4);
  175. int lo = in.readTTFUShort();
  176. if (log.isDebugEnabled()) {
  177. log.debug(tableTag + " lang sys tag: " + lt);
  178. log.debug(tableTag + " lang sys table offset: " + lo);
  179. }
  180. lta[i] = lt;
  181. loa[i] = lo;
  182. if (dl == lo) {
  183. dl = 0;
  184. dt = lt;
  185. }
  186. ll.add(lt);
  187. }
  188. // read non-default language system tables
  189. for (int i = 0, n = nl; i < n; i++) {
  190. readLangSysTable(tableTag, scriptTable + loa [ i ], lta [ i ]);
  191. }
  192. }
  193. // read default language system table (if specified)
  194. if (dl > 0) {
  195. readLangSysTable(tableTag, scriptTable + dl, dt);
  196. } else if (dt != null) {
  197. if (log.isDebugEnabled()) {
  198. log.debug(tableTag + " lang sys default: " + dt);
  199. }
  200. }
  201. seScripts.put(scriptTag, new Object[] { dt, ll, seLanguages });
  202. seLanguages = null;
  203. }
  204. private void readScriptList(OFTableName tableTag, long scriptList) throws IOException {
  205. in.seekSet(scriptList);
  206. // read script record count
  207. int ns = in.readTTFUShort();
  208. if (log.isDebugEnabled()) {
  209. log.debug(tableTag + " script list record count: " + ns);
  210. }
  211. if (ns > 0) {
  212. String[] sta = new String[ns];
  213. int[] soa = new int[ns];
  214. // read script records
  215. for (int i = 0, n = ns; i < n; i++) {
  216. String st = in.readTTFString(4);
  217. int so = in.readTTFUShort();
  218. if (log.isDebugEnabled()) {
  219. log.debug(tableTag + " script tag: " + st);
  220. log.debug(tableTag + " script table offset: " + so);
  221. }
  222. sta[i] = st;
  223. soa[i] = so;
  224. }
  225. // read script tables
  226. for (int i = 0, n = ns; i < n; i++) {
  227. seLanguages = null;
  228. readScriptTable(tableTag, scriptList + soa [ i ], sta [ i ]);
  229. }
  230. }
  231. }
  232. private void readFeatureTable(OFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
  233. in.seekSet(featureTable);
  234. if (log.isDebugEnabled()) {
  235. log.debug(tableTag + " feature table: " + featureTag);
  236. }
  237. // read feature params offset
  238. int po = in.readTTFUShort();
  239. // read lookup list indices count
  240. int nl = in.readTTFUShort();
  241. // dump info if debugging
  242. if (log.isDebugEnabled()) {
  243. log.debug(tableTag + " feature table parameters offset: " + po);
  244. log.debug(tableTag + " feature table lookup list index count: " + nl);
  245. }
  246. // read lookup table indices
  247. int[] lia = new int[nl];
  248. List lul = new java.util.ArrayList();
  249. for (int i = 0; i < nl; i++) {
  250. int li = in.readTTFUShort();
  251. if (log.isDebugEnabled()) {
  252. log.debug(tableTag + " feature table lookup index: " + li);
  253. }
  254. lia[i] = li;
  255. lul.add("lu" + li);
  256. }
  257. seFeatures.put("f" + featureIndex, new Object[] { featureTag, lul });
  258. }
  259. private void readFeatureList(OFTableName tableTag, long featureList) throws IOException {
  260. in.seekSet(featureList);
  261. // read feature record count
  262. int nf = in.readTTFUShort();
  263. if (log.isDebugEnabled()) {
  264. log.debug(tableTag + " feature list record count: " + nf);
  265. }
  266. if (nf > 0) {
  267. String[] fta = new String[nf];
  268. int[] foa = new int[nf];
  269. // read feature records
  270. for (int i = 0, n = nf; i < n; i++) {
  271. String ft = in.readTTFString(4);
  272. int fo = in.readTTFUShort();
  273. if (log.isDebugEnabled()) {
  274. log.debug(tableTag + " feature tag: " + ft);
  275. log.debug(tableTag + " feature table offset: " + fo);
  276. }
  277. fta[i] = ft;
  278. foa[i] = fo;
  279. }
  280. // read feature tables
  281. for (int i = 0, n = nf; i < n; i++) {
  282. if (log.isDebugEnabled()) {
  283. log.debug(tableTag + " feature index: " + i);
  284. }
  285. readFeatureTable(tableTag, featureList + foa [ i ], fta [ i ], i);
  286. }
  287. }
  288. }
  289. static final class GDEFLookupType {
  290. static final int GLYPH_CLASS = 1;
  291. static final int ATTACHMENT_POINT = 2;
  292. static final int LIGATURE_CARET = 3;
  293. static final int MARK_ATTACHMENT = 4;
  294. private GDEFLookupType() {
  295. }
  296. public static int getSubtableType(int lt) {
  297. int st;
  298. switch (lt) {
  299. case GDEFLookupType.GLYPH_CLASS:
  300. st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_GLYPH_CLASS;
  301. break;
  302. case GDEFLookupType.ATTACHMENT_POINT:
  303. st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_ATTACHMENT_POINT;
  304. break;
  305. case GDEFLookupType.LIGATURE_CARET:
  306. st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_LIGATURE_CARET;
  307. break;
  308. case GDEFLookupType.MARK_ATTACHMENT:
  309. st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_MARK_ATTACHMENT;
  310. break;
  311. default:
  312. st = -1;
  313. break;
  314. }
  315. return st;
  316. }
  317. public static String toString(int type) {
  318. String s;
  319. switch (type) {
  320. case GLYPH_CLASS:
  321. s = "GlyphClass";
  322. break;
  323. case ATTACHMENT_POINT:
  324. s = "AttachmentPoint";
  325. break;
  326. case LIGATURE_CARET:
  327. s = "LigatureCaret";
  328. break;
  329. case MARK_ATTACHMENT:
  330. s = "MarkAttachment";
  331. break;
  332. default:
  333. s = "?";
  334. break;
  335. }
  336. return s;
  337. }
  338. }
  339. static final class GSUBLookupType {
  340. static final int SINGLE = 1;
  341. static final int MULTIPLE = 2;
  342. static final int ALTERNATE = 3;
  343. static final int LIGATURE = 4;
  344. static final int CONTEXTUAL = 5;
  345. static final int CHAINED_CONTEXTUAL = 6;
  346. static final int EXTENSION = 7;
  347. static final int REVERSE_CHAINED_SINGLE = 8;
  348. private GSUBLookupType() {
  349. }
  350. public static int getSubtableType(int lt) {
  351. int st;
  352. switch (lt) {
  353. case GSUBLookupType.SINGLE:
  354. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_SINGLE;
  355. break;
  356. case GSUBLookupType.MULTIPLE:
  357. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_MULTIPLE;
  358. break;
  359. case GSUBLookupType.ALTERNATE:
  360. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_ALTERNATE;
  361. break;
  362. case GSUBLookupType.LIGATURE:
  363. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_LIGATURE;
  364. break;
  365. case GSUBLookupType.CONTEXTUAL:
  366. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CONTEXTUAL;
  367. break;
  368. case GSUBLookupType.CHAINED_CONTEXTUAL:
  369. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CHAINED_CONTEXTUAL;
  370. break;
  371. case GSUBLookupType.EXTENSION:
  372. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_EXTENSION_SUBSTITUTION;
  373. break;
  374. case GSUBLookupType.REVERSE_CHAINED_SINGLE:
  375. st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_REVERSE_CHAINED_SINGLE;
  376. break;
  377. default:
  378. st = -1;
  379. break;
  380. }
  381. return st;
  382. }
  383. public static String toString(int type) {
  384. String s;
  385. switch (type) {
  386. case SINGLE:
  387. s = "Single";
  388. break;
  389. case MULTIPLE:
  390. s = "Multiple";
  391. break;
  392. case ALTERNATE:
  393. s = "Alternate";
  394. break;
  395. case LIGATURE:
  396. s = "Ligature";
  397. break;
  398. case CONTEXTUAL:
  399. s = "Contextual";
  400. break;
  401. case CHAINED_CONTEXTUAL:
  402. s = "ChainedContextual";
  403. break;
  404. case EXTENSION:
  405. s = "Extension";
  406. break;
  407. case REVERSE_CHAINED_SINGLE:
  408. s = "ReverseChainedSingle";
  409. break;
  410. default:
  411. s = "?";
  412. break;
  413. }
  414. return s;
  415. }
  416. }
  417. static final class GPOSLookupType {
  418. static final int SINGLE = 1;
  419. static final int PAIR = 2;
  420. static final int CURSIVE = 3;
  421. static final int MARK_TO_BASE = 4;
  422. static final int MARK_TO_LIGATURE = 5;
  423. static final int MARK_TO_MARK = 6;
  424. static final int CONTEXTUAL = 7;
  425. static final int CHAINED_CONTEXTUAL = 8;
  426. static final int EXTENSION = 9;
  427. private GPOSLookupType() {
  428. }
  429. public static String toString(int type) {
  430. String s;
  431. switch (type) {
  432. case SINGLE:
  433. s = "Single";
  434. break;
  435. case PAIR:
  436. s = "Pair";
  437. break;
  438. case CURSIVE:
  439. s = "Cursive";
  440. break;
  441. case MARK_TO_BASE:
  442. s = "MarkToBase";
  443. break;
  444. case MARK_TO_LIGATURE:
  445. s = "MarkToLigature";
  446. break;
  447. case MARK_TO_MARK:
  448. s = "MarkToMark";
  449. break;
  450. case CONTEXTUAL:
  451. s = "Contextual";
  452. break;
  453. case CHAINED_CONTEXTUAL:
  454. s = "ChainedContextual";
  455. break;
  456. case EXTENSION:
  457. s = "Extension";
  458. break;
  459. default:
  460. s = "?";
  461. break;
  462. }
  463. return s;
  464. }
  465. }
  466. static final class LookupFlag {
  467. static final int RIGHT_TO_LEFT = 0x0001;
  468. static final int IGNORE_BASE_GLYPHS = 0x0002;
  469. static final int IGNORE_LIGATURE = 0x0004;
  470. static final int IGNORE_MARKS = 0x0008;
  471. static final int USE_MARK_FILTERING_SET = 0x0010;
  472. static final int MARK_ATTACHMENT_TYPE = 0xFF00;
  473. private LookupFlag() {
  474. }
  475. public static String toString(int flags) {
  476. StringBuffer sb = new StringBuffer();
  477. boolean first = true;
  478. if ((flags & RIGHT_TO_LEFT) != 0) {
  479. if (first) {
  480. first = false;
  481. } else {
  482. sb.append('|');
  483. }
  484. sb.append("RightToLeft");
  485. }
  486. if ((flags & IGNORE_BASE_GLYPHS) != 0) {
  487. if (first) {
  488. first = false;
  489. } else {
  490. sb.append('|');
  491. }
  492. sb.append("IgnoreBaseGlyphs");
  493. }
  494. if ((flags & IGNORE_LIGATURE) != 0) {
  495. if (first) {
  496. first = false;
  497. } else {
  498. sb.append('|');
  499. }
  500. sb.append("IgnoreLigature");
  501. }
  502. if ((flags & IGNORE_MARKS) != 0) {
  503. if (first) {
  504. first = false;
  505. } else {
  506. sb.append('|');
  507. }
  508. sb.append("IgnoreMarks");
  509. }
  510. if ((flags & USE_MARK_FILTERING_SET) != 0) {
  511. if (first) {
  512. first = false;
  513. } else {
  514. sb.append('|');
  515. }
  516. sb.append("UseMarkFilteringSet");
  517. }
  518. if (sb.length() == 0) {
  519. sb.append('-');
  520. }
  521. return sb.toString();
  522. }
  523. }
  524. private GlyphCoverageTable readCoverageTableFormat1(String label, long tableOffset, int coverageFormat) throws IOException {
  525. List entries = new java.util.ArrayList();
  526. in.seekSet(tableOffset);
  527. // skip over format (already known)
  528. in.skip(2);
  529. // read glyph count
  530. int ng = in.readTTFUShort();
  531. int[] ga = new int[ng];
  532. for (int i = 0, n = ng; i < n; i++) {
  533. int g = in.readTTFUShort();
  534. ga[i] = g;
  535. entries.add(Integer.valueOf(g));
  536. }
  537. // dump info if debugging
  538. if (log.isDebugEnabled()) {
  539. log.debug(label + " glyphs: " + toString(ga));
  540. }
  541. return GlyphCoverageTable.createCoverageTable(entries);
  542. }
  543. private GlyphCoverageTable readCoverageTableFormat2(String label, long tableOffset, int coverageFormat) throws IOException {
  544. List entries = new java.util.ArrayList();
  545. in.seekSet(tableOffset);
  546. // skip over format (already known)
  547. in.skip(2);
  548. // read range record count
  549. int nr = in.readTTFUShort();
  550. for (int i = 0, n = nr; i < n; i++) {
  551. // read range start
  552. int s = in.readTTFUShort();
  553. // read range end
  554. int e = in.readTTFUShort();
  555. // read range coverage (mapping) index
  556. int m = in.readTTFUShort();
  557. // dump info if debugging
  558. if (log.isDebugEnabled()) {
  559. log.debug(label + " range[" + i + "]: [" + s + "," + e + "]: " + m);
  560. }
  561. entries.add(new GlyphCoverageTable.MappingRange(s, e, m));
  562. }
  563. return GlyphCoverageTable.createCoverageTable(entries);
  564. }
  565. private GlyphCoverageTable readCoverageTable(String label, long tableOffset) throws IOException {
  566. GlyphCoverageTable gct;
  567. long cp = in.getCurrentPos();
  568. in.seekSet(tableOffset);
  569. // read coverage table format
  570. int cf = in.readTTFUShort();
  571. if (cf == 1) {
  572. gct = readCoverageTableFormat1(label, tableOffset, cf);
  573. } else if (cf == 2) {
  574. gct = readCoverageTableFormat2(label, tableOffset, cf);
  575. } else {
  576. throw new AdvancedTypographicTableFormatException("unsupported coverage table format: " + cf);
  577. }
  578. in.seekSet(cp);
  579. return gct;
  580. }
  581. private GlyphClassTable readClassDefTableFormat1(String label, long tableOffset, int classFormat) throws IOException {
  582. List entries = new java.util.ArrayList();
  583. in.seekSet(tableOffset);
  584. // skip over format (already known)
  585. in.skip(2);
  586. // read start glyph
  587. int sg = in.readTTFUShort();
  588. entries.add(Integer.valueOf(sg));
  589. // read glyph count
  590. int ng = in.readTTFUShort();
  591. // read glyph classes
  592. int[] ca = new int[ng];
  593. for (int i = 0, n = ng; i < n; i++) {
  594. int gc = in.readTTFUShort();
  595. ca[i] = gc;
  596. entries.add(Integer.valueOf(gc));
  597. }
  598. // dump info if debugging
  599. if (log.isDebugEnabled()) {
  600. log.debug(label + " glyph classes: " + toString(ca));
  601. }
  602. return GlyphClassTable.createClassTable(entries);
  603. }
  604. private GlyphClassTable readClassDefTableFormat2(String label, long tableOffset, int classFormat) throws IOException {
  605. List entries = new java.util.ArrayList();
  606. in.seekSet(tableOffset);
  607. // skip over format (already known)
  608. in.skip(2);
  609. // read range record count
  610. int nr = in.readTTFUShort();
  611. for (int i = 0, n = nr; i < n; i++) {
  612. // read range start
  613. int s = in.readTTFUShort();
  614. // read range end
  615. int e = in.readTTFUShort();
  616. // read range glyph class (mapping) index
  617. int m = in.readTTFUShort();
  618. // dump info if debugging
  619. if (log.isDebugEnabled()) {
  620. log.debug(label + " range[" + i + "]: [" + s + "," + e + "]: " + m);
  621. }
  622. entries.add(new GlyphClassTable.MappingRange(s, e, m));
  623. }
  624. return GlyphClassTable.createClassTable(entries);
  625. }
  626. private GlyphClassTable readClassDefTable(String label, long tableOffset) throws IOException {
  627. GlyphClassTable gct;
  628. long cp = in.getCurrentPos();
  629. in.seekSet(tableOffset);
  630. // read class table format
  631. int cf = in.readTTFUShort();
  632. if (cf == 1) {
  633. gct = readClassDefTableFormat1(label, tableOffset, cf);
  634. } else if (cf == 2) {
  635. gct = readClassDefTableFormat2(label, tableOffset, cf);
  636. } else {
  637. throw new AdvancedTypographicTableFormatException("unsupported class definition table format: " + cf);
  638. }
  639. in.seekSet(cp);
  640. return gct;
  641. }
  642. private void readSingleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  643. String tableTag = "GSUB";
  644. in.seekSet(subtableOffset);
  645. // skip over format (already known)
  646. in.skip(2);
  647. // read coverage offset
  648. int co = in.readTTFUShort();
  649. // read delta glyph
  650. int dg = in.readTTFShort();
  651. // dump info if debugging
  652. if (log.isDebugEnabled()) {
  653. log.debug(tableTag + " single substitution subtable format: " + subtableFormat + " (delta)");
  654. log.debug(tableTag + " single substitution coverage table offset: " + co);
  655. log.debug(tableTag + " single substitution delta: " + dg);
  656. }
  657. // read coverage table
  658. seMapping = readCoverageTable(tableTag + " single substitution coverage", subtableOffset + co);
  659. seEntries.add(Integer.valueOf(dg));
  660. }
  661. private void readSingleSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  662. String tableTag = "GSUB";
  663. in.seekSet(subtableOffset);
  664. // skip over format (already known)
  665. in.skip(2);
  666. // read coverage offset
  667. int co = in.readTTFUShort();
  668. // read glyph count
  669. int ng = in.readTTFUShort();
  670. // dump info if debugging
  671. if (log.isDebugEnabled()) {
  672. log.debug(tableTag + " single substitution subtable format: " + subtableFormat + " (mapped)");
  673. log.debug(tableTag + " single substitution coverage table offset: " + co);
  674. log.debug(tableTag + " single substitution glyph count: " + ng);
  675. }
  676. // read coverage table
  677. seMapping = readCoverageTable(tableTag + " single substitution coverage", subtableOffset + co);
  678. // read glyph substitutions
  679. int[] gsa = new int[ng];
  680. for (int i = 0, n = ng; i < n; i++) {
  681. int gs = in.readTTFUShort();
  682. if (log.isDebugEnabled()) {
  683. log.debug(tableTag + " single substitution glyph[" + i + "]: " + gs);
  684. }
  685. gsa[i] = gs;
  686. seEntries.add(Integer.valueOf(gs));
  687. }
  688. }
  689. private int readSingleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  690. in.seekSet(subtableOffset);
  691. // read substitution subtable format
  692. int sf = in.readTTFUShort();
  693. if (sf == 1) {
  694. readSingleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  695. } else if (sf == 2) {
  696. readSingleSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  697. } else {
  698. throw new AdvancedTypographicTableFormatException("unsupported single substitution subtable format: " + sf);
  699. }
  700. return sf;
  701. }
  702. private void readMultipleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  703. String tableTag = "GSUB";
  704. in.seekSet(subtableOffset);
  705. // skip over format (already known)
  706. in.skip(2);
  707. // read coverage offset
  708. int co = in.readTTFUShort();
  709. // read sequence count
  710. int ns = in.readTTFUShort();
  711. // dump info if debugging
  712. if (log.isDebugEnabled()) {
  713. log.debug(tableTag + " multiple substitution subtable format: " + subtableFormat + " (mapped)");
  714. log.debug(tableTag + " multiple substitution coverage table offset: " + co);
  715. log.debug(tableTag + " multiple substitution sequence count: " + ns);
  716. }
  717. // read coverage table
  718. seMapping = readCoverageTable(tableTag + " multiple substitution coverage", subtableOffset + co);
  719. // read sequence table offsets
  720. int[] soa = new int[ns];
  721. for (int i = 0, n = ns; i < n; i++) {
  722. soa[i] = in.readTTFUShort();
  723. }
  724. // read sequence tables
  725. int[][] gsa = new int [ ns ] [];
  726. for (int i = 0, n = ns; i < n; i++) {
  727. int so = soa[i];
  728. int[] ga;
  729. if (so > 0) {
  730. in.seekSet(subtableOffset + so);
  731. // read glyph count
  732. int ng = in.readTTFUShort();
  733. ga = new int[ng];
  734. for (int j = 0; j < ng; j++) {
  735. ga[j] = in.readTTFUShort();
  736. }
  737. } else {
  738. ga = null;
  739. }
  740. if (log.isDebugEnabled()) {
  741. log.debug(tableTag + " multiple substitution sequence[" + i + "]: " + toString(ga));
  742. }
  743. gsa [ i ] = ga;
  744. }
  745. seEntries.add(gsa);
  746. }
  747. private int readMultipleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  748. in.seekSet(subtableOffset);
  749. // read substitution subtable format
  750. int sf = in.readTTFUShort();
  751. if (sf == 1) {
  752. readMultipleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  753. } else {
  754. throw new AdvancedTypographicTableFormatException("unsupported multiple substitution subtable format: " + sf);
  755. }
  756. return sf;
  757. }
  758. private void readAlternateSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  759. String tableTag = "GSUB";
  760. in.seekSet(subtableOffset);
  761. // skip over format (already known)
  762. in.skip(2);
  763. // read coverage offset
  764. int co = in.readTTFUShort();
  765. // read alternate set count
  766. int ns = in.readTTFUShort();
  767. // dump info if debugging
  768. if (log.isDebugEnabled()) {
  769. log.debug(tableTag + " alternate substitution subtable format: " + subtableFormat + " (mapped)");
  770. log.debug(tableTag + " alternate substitution coverage table offset: " + co);
  771. log.debug(tableTag + " alternate substitution alternate set count: " + ns);
  772. }
  773. // read coverage table
  774. seMapping = readCoverageTable(tableTag + " alternate substitution coverage", subtableOffset + co);
  775. // read alternate set table offsets
  776. int[] soa = new int[ns];
  777. for (int i = 0, n = ns; i < n; i++) {
  778. soa[i] = in.readTTFUShort();
  779. }
  780. // read alternate set tables
  781. for (int i = 0, n = ns; i < n; i++) {
  782. int so = soa[i];
  783. in.seekSet(subtableOffset + so);
  784. // read glyph count
  785. int ng = in.readTTFUShort();
  786. int[] ga = new int[ng];
  787. for (int j = 0; j < ng; j++) {
  788. int gs = in.readTTFUShort();
  789. ga[j] = gs;
  790. }
  791. if (log.isDebugEnabled()) {
  792. log.debug(tableTag + " alternate substitution alternate set[" + i + "]: " + toString(ga));
  793. }
  794. seEntries.add(ga);
  795. }
  796. }
  797. private int readAlternateSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  798. in.seekSet(subtableOffset);
  799. // read substitution subtable format
  800. int sf = in.readTTFUShort();
  801. if (sf == 1) {
  802. readAlternateSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  803. } else {
  804. throw new AdvancedTypographicTableFormatException("unsupported alternate substitution subtable format: " + sf);
  805. }
  806. return sf;
  807. }
  808. private void readLigatureSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  809. String tableTag = "GSUB";
  810. in.seekSet(subtableOffset);
  811. // skip over format (already known)
  812. in.skip(2);
  813. // read coverage offset
  814. int co = in.readTTFUShort();
  815. // read ligature set count
  816. int ns = in.readTTFUShort();
  817. // dump info if debugging
  818. if (log.isDebugEnabled()) {
  819. log.debug(tableTag + " ligature substitution subtable format: " + subtableFormat + " (mapped)");
  820. log.debug(tableTag + " ligature substitution coverage table offset: " + co);
  821. log.debug(tableTag + " ligature substitution ligature set count: " + ns);
  822. }
  823. // read coverage table
  824. seMapping = readCoverageTable(tableTag + " ligature substitution coverage", subtableOffset + co);
  825. // read ligature set table offsets
  826. int[] soa = new int[ns];
  827. for (int i = 0, n = ns; i < n; i++) {
  828. soa[i] = in.readTTFUShort();
  829. }
  830. // read ligature set tables
  831. for (int i = 0, n = ns; i < n; i++) {
  832. int so = soa[i];
  833. in.seekSet(subtableOffset + so);
  834. // read ligature table count
  835. int nl = in.readTTFUShort();
  836. int[] loa = new int[nl];
  837. for (int j = 0; j < nl; j++) {
  838. loa[j] = in.readTTFUShort();
  839. }
  840. List ligs = new java.util.ArrayList();
  841. for (int j = 0; j < nl; j++) {
  842. int lo = loa[j];
  843. in.seekSet(subtableOffset + so + lo);
  844. // read ligature glyph id
  845. int lg = in.readTTFUShort();
  846. // read ligature (input) component count
  847. int nc = in.readTTFUShort();
  848. int[] ca = new int [ nc - 1 ];
  849. // read ligature (input) component glyph ids
  850. for (int k = 0; k < nc - 1; k++) {
  851. ca[k] = in.readTTFUShort();
  852. }
  853. if (log.isDebugEnabled()) {
  854. log.debug(tableTag + " ligature substitution ligature set[" + i + "]: ligature(" + lg + "), components: " + toString(ca));
  855. }
  856. ligs.add(new GlyphSubstitutionTable.Ligature(lg, ca));
  857. }
  858. seEntries.add(new GlyphSubstitutionTable.LigatureSet(ligs));
  859. }
  860. }
  861. private int readLigatureSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  862. in.seekSet(subtableOffset);
  863. // read substitution subtable format
  864. int sf = in.readTTFUShort();
  865. if (sf == 1) {
  866. readLigatureSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  867. } else {
  868. throw new AdvancedTypographicTableFormatException("unsupported ligature substitution subtable format: " + sf);
  869. }
  870. return sf;
  871. }
  872. private GlyphTable.RuleLookup[] readRuleLookups(int numLookups, String header) throws IOException {
  873. GlyphTable.RuleLookup[] la = new GlyphTable.RuleLookup [ numLookups ];
  874. for (int i = 0, n = numLookups; i < n; i++) {
  875. int sequenceIndex = in.readTTFUShort();
  876. int lookupIndex = in.readTTFUShort();
  877. la [ i ] = new GlyphTable.RuleLookup(sequenceIndex, lookupIndex);
  878. // dump info if debugging and header is non-null
  879. if (log.isDebugEnabled() && (header != null)) {
  880. log.debug(header + "lookup[" + i + "]: " + la[i]);
  881. }
  882. }
  883. return la;
  884. }
  885. private void readContextualSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  886. String tableTag = "GSUB";
  887. in.seekSet(subtableOffset);
  888. // skip over format (already known)
  889. in.skip(2);
  890. // read coverage offset
  891. int co = in.readTTFUShort();
  892. // read rule set count
  893. int nrs = in.readTTFUShort();
  894. // read rule set offsets
  895. int[] rsoa = new int [ nrs ];
  896. for (int i = 0; i < nrs; i++) {
  897. rsoa [ i ] = in.readTTFUShort();
  898. }
  899. // dump info if debugging
  900. if (log.isDebugEnabled()) {
  901. log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyphs)");
  902. log.debug(tableTag + " contextual substitution coverage table offset: " + co);
  903. log.debug(tableTag + " contextual substitution rule set count: " + nrs);
  904. for (int i = 0; i < nrs; i++) {
  905. log.debug(tableTag + " contextual substitution rule set offset[" + i + "]: " + rsoa[i]);
  906. }
  907. }
  908. // read coverage table
  909. GlyphCoverageTable ct;
  910. if (co > 0) {
  911. ct = readCoverageTable(tableTag + " contextual substitution coverage", subtableOffset + co);
  912. } else {
  913. ct = null;
  914. }
  915. // read rule sets
  916. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ];
  917. String header = null;
  918. for (int i = 0; i < nrs; i++) {
  919. GlyphTable.RuleSet rs;
  920. int rso = rsoa [ i ];
  921. if (rso > 0) {
  922. // seek to rule set [ i ]
  923. in.seekSet(subtableOffset + rso);
  924. // read rule count
  925. int nr = in.readTTFUShort();
  926. // read rule offsets
  927. int[] roa = new int [ nr ];
  928. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  929. for (int j = 0; j < nr; j++) {
  930. roa [ j ] = in.readTTFUShort();
  931. }
  932. // read glyph sequence rules
  933. for (int j = 0; j < nr; j++) {
  934. GlyphTable.GlyphSequenceRule r;
  935. int ro = roa [ j ];
  936. if (ro > 0) {
  937. // seek to rule [ j ]
  938. in.seekSet(subtableOffset + rso + ro);
  939. // read glyph count
  940. int ng = in.readTTFUShort();
  941. // read rule lookup count
  942. int nl = in.readTTFUShort();
  943. // read glyphs
  944. int[] glyphs = new int [ ng - 1 ];
  945. for (int k = 0, nk = glyphs.length; k < nk; k++) {
  946. glyphs [ k ] = in.readTTFUShort();
  947. }
  948. // read rule lookups
  949. if (log.isDebugEnabled()) {
  950. header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: ";
  951. }
  952. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  953. r = new GlyphTable.GlyphSequenceRule(lookups, ng, glyphs);
  954. } else {
  955. r = null;
  956. }
  957. ra [ j ] = r;
  958. }
  959. rs = new GlyphTable.HomogeneousRuleSet(ra);
  960. } else {
  961. rs = null;
  962. }
  963. rsa [ i ] = rs;
  964. }
  965. // store results
  966. seMapping = ct;
  967. seEntries.add(rsa);
  968. }
  969. private void readContextualSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  970. String tableTag = "GSUB";
  971. in.seekSet(subtableOffset);
  972. // skip over format (already known)
  973. in.skip(2);
  974. // read coverage offset
  975. int co = in.readTTFUShort();
  976. // read class def table offset
  977. int cdo = in.readTTFUShort();
  978. // read class rule set count
  979. int ngc = in.readTTFUShort();
  980. // read class rule set offsets
  981. int[] csoa = new int [ ngc ];
  982. for (int i = 0; i < ngc; i++) {
  983. csoa [ i ] = in.readTTFUShort();
  984. }
  985. // dump info if debugging
  986. if (log.isDebugEnabled()) {
  987. log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyph classes)");
  988. log.debug(tableTag + " contextual substitution coverage table offset: " + co);
  989. log.debug(tableTag + " contextual substitution class set count: " + ngc);
  990. for (int i = 0; i < ngc; i++) {
  991. log.debug(tableTag + " contextual substitution class set offset[" + i + "]: " + csoa[i]);
  992. }
  993. }
  994. // read coverage table
  995. GlyphCoverageTable ct;
  996. if (co > 0) {
  997. ct = readCoverageTable(tableTag + " contextual substitution coverage", subtableOffset + co);
  998. } else {
  999. ct = null;
  1000. }
  1001. // read class definition table
  1002. GlyphClassTable cdt;
  1003. if (cdo > 0) {
  1004. cdt = readClassDefTable(tableTag + " contextual substitution class definition", subtableOffset + cdo);
  1005. } else {
  1006. cdt = null;
  1007. }
  1008. // read rule sets
  1009. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ];
  1010. String header = null;
  1011. for (int i = 0; i < ngc; i++) {
  1012. int cso = csoa [ i ];
  1013. GlyphTable.RuleSet rs;
  1014. if (cso > 0) {
  1015. // seek to rule set [ i ]
  1016. in.seekSet(subtableOffset + cso);
  1017. // read rule count
  1018. int nr = in.readTTFUShort();
  1019. // read rule offsets
  1020. int[] roa = new int [ nr ];
  1021. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  1022. for (int j = 0; j < nr; j++) {
  1023. roa [ j ] = in.readTTFUShort();
  1024. }
  1025. // read glyph sequence rules
  1026. for (int j = 0; j < nr; j++) {
  1027. int ro = roa [ j ];
  1028. GlyphTable.ClassSequenceRule r;
  1029. if (ro > 0) {
  1030. // seek to rule [ j ]
  1031. in.seekSet(subtableOffset + cso + ro);
  1032. // read glyph count
  1033. int ng = in.readTTFUShort();
  1034. // read rule lookup count
  1035. int nl = in.readTTFUShort();
  1036. // read classes
  1037. int[] classes = new int [ ng - 1 ];
  1038. for (int k = 0, nk = classes.length; k < nk; k++) {
  1039. classes [ k ] = in.readTTFUShort();
  1040. }
  1041. // read rule lookups
  1042. if (log.isDebugEnabled()) {
  1043. header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: ";
  1044. }
  1045. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  1046. r = new GlyphTable.ClassSequenceRule(lookups, ng, classes);
  1047. } else {
  1048. assert ro > 0 : "unexpected null subclass rule offset";
  1049. r = null;
  1050. }
  1051. ra [ j ] = r;
  1052. }
  1053. rs = new GlyphTable.HomogeneousRuleSet(ra);
  1054. } else {
  1055. rs = null;
  1056. }
  1057. rsa [ i ] = rs;
  1058. }
  1059. // store results
  1060. seMapping = ct;
  1061. seEntries.add(cdt);
  1062. seEntries.add(Integer.valueOf(ngc));
  1063. seEntries.add(rsa);
  1064. }
  1065. private void readContextualSubTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1066. String tableTag = "GSUB";
  1067. in.seekSet(subtableOffset);
  1068. // skip over format (already known)
  1069. in.skip(2);
  1070. // read glyph (input sequence length) count
  1071. int ng = in.readTTFUShort();
  1072. // read substitution lookup count
  1073. int nl = in.readTTFUShort();
  1074. // read glyph coverage offsets, one per glyph input sequence length count
  1075. int[] gcoa = new int [ ng ];
  1076. for (int i = 0; i < ng; i++) {
  1077. gcoa [ i ] = in.readTTFUShort();
  1078. }
  1079. // dump info if debugging
  1080. if (log.isDebugEnabled()) {
  1081. log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyph sets)");
  1082. log.debug(tableTag + " contextual substitution glyph input sequence length count: " + ng);
  1083. log.debug(tableTag + " contextual substitution lookup count: " + nl);
  1084. for (int i = 0; i < ng; i++) {
  1085. log.debug(tableTag + " contextual substitution coverage table offset[" + i + "]: " + gcoa[i]);
  1086. }
  1087. }
  1088. // read coverage tables
  1089. GlyphCoverageTable[] gca = new GlyphCoverageTable [ ng ];
  1090. for (int i = 0; i < ng; i++) {
  1091. int gco = gcoa [ i ];
  1092. GlyphCoverageTable gct;
  1093. if (gco > 0) {
  1094. gct = readCoverageTable(tableTag + " contextual substitution coverage[" + i + "]", subtableOffset + gco);
  1095. } else {
  1096. gct = null;
  1097. }
  1098. gca [ i ] = gct;
  1099. }
  1100. // read rule lookups
  1101. String header = null;
  1102. if (log.isDebugEnabled()) {
  1103. header = tableTag + " contextual substitution lookups: ";
  1104. }
  1105. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  1106. // construct rule, rule set, and rule set array
  1107. GlyphTable.Rule r = new GlyphTable.CoverageSequenceRule(lookups, ng, gca);
  1108. GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r});
  1109. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs};
  1110. // store results
  1111. assert (gca != null) && (gca.length > 0);
  1112. seMapping = gca[0];
  1113. seEntries.add(rsa);
  1114. }
  1115. private int readContextualSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  1116. in.seekSet(subtableOffset);
  1117. // read substitution subtable format
  1118. int sf = in.readTTFUShort();
  1119. if (sf == 1) {
  1120. readContextualSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  1121. } else if (sf == 2) {
  1122. readContextualSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  1123. } else if (sf == 3) {
  1124. readContextualSubTableFormat3(lookupType, lookupFlags, subtableOffset, sf);
  1125. } else {
  1126. throw new AdvancedTypographicTableFormatException("unsupported contextual substitution subtable format: " + sf);
  1127. }
  1128. return sf;
  1129. }
  1130. private void readChainedContextualSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1131. String tableTag = "GSUB";
  1132. in.seekSet(subtableOffset);
  1133. // skip over format (already known)
  1134. in.skip(2);
  1135. // read coverage offset
  1136. int co = in.readTTFUShort();
  1137. // read rule set count
  1138. int nrs = in.readTTFUShort();
  1139. // read rule set offsets
  1140. int[] rsoa = new int [ nrs ];
  1141. for (int i = 0; i < nrs; i++) {
  1142. rsoa [ i ] = in.readTTFUShort();
  1143. }
  1144. // dump info if debugging
  1145. if (log.isDebugEnabled()) {
  1146. log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyphs)");
  1147. log.debug(tableTag + " chained contextual substitution coverage table offset: " + co);
  1148. log.debug(tableTag + " chained contextual substitution rule set count: " + nrs);
  1149. for (int i = 0; i < nrs; i++) {
  1150. log.debug(tableTag + " chained contextual substitution rule set offset[" + i + "]: " + rsoa[i]);
  1151. }
  1152. }
  1153. // read coverage table
  1154. GlyphCoverageTable ct;
  1155. if (co > 0) {
  1156. ct = readCoverageTable(tableTag + " chained contextual substitution coverage", subtableOffset + co);
  1157. } else {
  1158. ct = null;
  1159. }
  1160. // read rule sets
  1161. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ];
  1162. String header = null;
  1163. for (int i = 0; i < nrs; i++) {
  1164. GlyphTable.RuleSet rs;
  1165. int rso = rsoa [ i ];
  1166. if (rso > 0) {
  1167. // seek to rule set [ i ]
  1168. in.seekSet(subtableOffset + rso);
  1169. // read rule count
  1170. int nr = in.readTTFUShort();
  1171. // read rule offsets
  1172. int[] roa = new int [ nr ];
  1173. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  1174. for (int j = 0; j < nr; j++) {
  1175. roa [ j ] = in.readTTFUShort();
  1176. }
  1177. // read glyph sequence rules
  1178. for (int j = 0; j < nr; j++) {
  1179. GlyphTable.ChainedGlyphSequenceRule r;
  1180. int ro = roa [ j ];
  1181. if (ro > 0) {
  1182. // seek to rule [ j ]
  1183. in.seekSet(subtableOffset + rso + ro);
  1184. // read backtrack glyph count
  1185. int nbg = in.readTTFUShort();
  1186. // read backtrack glyphs
  1187. int[] backtrackGlyphs = new int [ nbg ];
  1188. for (int k = 0, nk = backtrackGlyphs.length; k < nk; k++) {
  1189. backtrackGlyphs [ k ] = in.readTTFUShort();
  1190. }
  1191. // read input glyph count
  1192. int nig = in.readTTFUShort();
  1193. // read glyphs
  1194. int[] glyphs = new int [ nig - 1 ];
  1195. for (int k = 0, nk = glyphs.length; k < nk; k++) {
  1196. glyphs [ k ] = in.readTTFUShort();
  1197. }
  1198. // read lookahead glyph count
  1199. int nlg = in.readTTFUShort();
  1200. // read lookahead glyphs
  1201. int[] lookaheadGlyphs = new int [ nlg ];
  1202. for (int k = 0, nk = lookaheadGlyphs.length; k < nk; k++) {
  1203. lookaheadGlyphs [ k ] = in.readTTFUShort();
  1204. }
  1205. // read rule lookup count
  1206. int nl = in.readTTFUShort();
  1207. // read rule lookups
  1208. if (log.isDebugEnabled()) {
  1209. header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: ";
  1210. }
  1211. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  1212. r = new GlyphTable.ChainedGlyphSequenceRule(lookups, nig, glyphs, backtrackGlyphs, lookaheadGlyphs);
  1213. } else {
  1214. r = null;
  1215. }
  1216. ra [ j ] = r;
  1217. }
  1218. rs = new GlyphTable.HomogeneousRuleSet(ra);
  1219. } else {
  1220. rs = null;
  1221. }
  1222. rsa [ i ] = rs;
  1223. }
  1224. // store results
  1225. seMapping = ct;
  1226. seEntries.add(rsa);
  1227. }
  1228. private void readChainedContextualSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1229. String tableTag = "GSUB";
  1230. in.seekSet(subtableOffset);
  1231. // skip over format (already known)
  1232. in.skip(2);
  1233. // read coverage offset
  1234. int co = in.readTTFUShort();
  1235. // read backtrack class def table offset
  1236. int bcdo = in.readTTFUShort();
  1237. // read input class def table offset
  1238. int icdo = in.readTTFUShort();
  1239. // read lookahead class def table offset
  1240. int lcdo = in.readTTFUShort();
  1241. // read class set count
  1242. int ngc = in.readTTFUShort();
  1243. // read class set offsets
  1244. int[] csoa = new int [ ngc ];
  1245. for (int i = 0; i < ngc; i++) {
  1246. csoa [ i ] = in.readTTFUShort();
  1247. }
  1248. // dump info if debugging
  1249. if (log.isDebugEnabled()) {
  1250. log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyph classes)");
  1251. log.debug(tableTag + " chained contextual substitution coverage table offset: " + co);
  1252. log.debug(tableTag + " chained contextual substitution class set count: " + ngc);
  1253. for (int i = 0; i < ngc; i++) {
  1254. log.debug(tableTag + " chained contextual substitution class set offset[" + i + "]: " + csoa[i]);
  1255. }
  1256. }
  1257. // read coverage table
  1258. GlyphCoverageTable ct;
  1259. if (co > 0) {
  1260. ct = readCoverageTable(tableTag + " chained contextual substitution coverage", subtableOffset + co);
  1261. } else {
  1262. ct = null;
  1263. }
  1264. // read backtrack class definition table
  1265. GlyphClassTable bcdt;
  1266. if (bcdo > 0) {
  1267. bcdt = readClassDefTable(tableTag + " contextual substitution backtrack class definition", subtableOffset + bcdo);
  1268. } else {
  1269. bcdt = null;
  1270. }
  1271. // read input class definition table
  1272. GlyphClassTable icdt;
  1273. if (icdo > 0) {
  1274. icdt = readClassDefTable(tableTag + " contextual substitution input class definition", subtableOffset + icdo);
  1275. } else {
  1276. icdt = null;
  1277. }
  1278. // read lookahead class definition table
  1279. GlyphClassTable lcdt;
  1280. if (lcdo > 0) {
  1281. lcdt = readClassDefTable(tableTag + " contextual substitution lookahead class definition", subtableOffset + lcdo);
  1282. } else {
  1283. lcdt = null;
  1284. }
  1285. // read rule sets
  1286. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ];
  1287. String header = null;
  1288. for (int i = 0; i < ngc; i++) {
  1289. int cso = csoa [ i ];
  1290. GlyphTable.RuleSet rs;
  1291. if (cso > 0) {
  1292. // seek to rule set [ i ]
  1293. in.seekSet(subtableOffset + cso);
  1294. // read rule count
  1295. int nr = in.readTTFUShort();
  1296. // read rule offsets
  1297. int[] roa = new int [ nr ];
  1298. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  1299. for (int j = 0; j < nr; j++) {
  1300. roa [ j ] = in.readTTFUShort();
  1301. }
  1302. // read glyph sequence rules
  1303. for (int j = 0; j < nr; j++) {
  1304. int ro = roa [ j ];
  1305. GlyphTable.ChainedClassSequenceRule r;
  1306. if (ro > 0) {
  1307. // seek to rule [ j ]
  1308. in.seekSet(subtableOffset + cso + ro);
  1309. // read backtrack glyph class count
  1310. int nbc = in.readTTFUShort();
  1311. // read backtrack glyph classes
  1312. int[] backtrackClasses = new int [ nbc ];
  1313. for (int k = 0, nk = backtrackClasses.length; k < nk; k++) {
  1314. backtrackClasses [ k ] = in.readTTFUShort();
  1315. }
  1316. // read input glyph class count
  1317. int nic = in.readTTFUShort();
  1318. // read input glyph classes
  1319. int[] classes = new int [ nic - 1 ];
  1320. for (int k = 0, nk = classes.length; k < nk; k++) {
  1321. classes [ k ] = in.readTTFUShort();
  1322. }
  1323. // read lookahead glyph class count
  1324. int nlc = in.readTTFUShort();
  1325. // read lookahead glyph classes
  1326. int[] lookaheadClasses = new int [ nlc ];
  1327. for (int k = 0, nk = lookaheadClasses.length; k < nk; k++) {
  1328. lookaheadClasses [ k ] = in.readTTFUShort();
  1329. }
  1330. // read rule lookup count
  1331. int nl = in.readTTFUShort();
  1332. // read rule lookups
  1333. if (log.isDebugEnabled()) {
  1334. header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: ";
  1335. }
  1336. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  1337. r = new GlyphTable.ChainedClassSequenceRule(lookups, nic, classes, backtrackClasses, lookaheadClasses);
  1338. } else {
  1339. r = null;
  1340. }
  1341. ra [ j ] = r;
  1342. }
  1343. rs = new GlyphTable.HomogeneousRuleSet(ra);
  1344. } else {
  1345. rs = null;
  1346. }
  1347. rsa [ i ] = rs;
  1348. }
  1349. // store results
  1350. seMapping = ct;
  1351. seEntries.add(icdt);
  1352. seEntries.add(bcdt);
  1353. seEntries.add(lcdt);
  1354. seEntries.add(Integer.valueOf(ngc));
  1355. seEntries.add(rsa);
  1356. }
  1357. private void readChainedContextualSubTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1358. String tableTag = "GSUB";
  1359. in.seekSet(subtableOffset);
  1360. // skip over format (already known)
  1361. in.skip(2);
  1362. // read backtrack glyph count
  1363. int nbg = in.readTTFUShort();
  1364. // read backtrack glyph coverage offsets
  1365. int[] bgcoa = new int [ nbg ];
  1366. for (int i = 0; i < nbg; i++) {
  1367. bgcoa [ i ] = in.readTTFUShort();
  1368. }
  1369. // read input glyph count
  1370. int nig = in.readTTFUShort();
  1371. // read input glyph coverage offsets
  1372. int[] igcoa = new int [ nig ];
  1373. for (int i = 0; i < nig; i++) {
  1374. igcoa [ i ] = in.readTTFUShort();
  1375. }
  1376. // read lookahead glyph count
  1377. int nlg = in.readTTFUShort();
  1378. // read lookahead glyph coverage offsets
  1379. int[] lgcoa = new int [ nlg ];
  1380. for (int i = 0; i < nlg; i++) {
  1381. lgcoa [ i ] = in.readTTFUShort();
  1382. }
  1383. // read substitution lookup count
  1384. int nl = in.readTTFUShort();
  1385. // dump info if debugging
  1386. if (log.isDebugEnabled()) {
  1387. log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyph sets)");
  1388. log.debug(tableTag + " chained contextual substitution backtrack glyph count: " + nbg);
  1389. for (int i = 0; i < nbg; i++) {
  1390. log.debug(tableTag + " chained contextual substitution backtrack coverage table offset[" + i + "]: " + bgcoa[i]);
  1391. }
  1392. log.debug(tableTag + " chained contextual substitution input glyph count: " + nig);
  1393. for (int i = 0; i < nig; i++) {
  1394. log.debug(tableTag + " chained contextual substitution input coverage table offset[" + i + "]: " + igcoa[i]);
  1395. }
  1396. log.debug(tableTag + " chained contextual substitution lookahead glyph count: " + nlg);
  1397. for (int i = 0; i < nlg; i++) {
  1398. log.debug(tableTag + " chained contextual substitution lookahead coverage table offset[" + i + "]: " + lgcoa[i]);
  1399. }
  1400. log.debug(tableTag + " chained contextual substitution lookup count: " + nl);
  1401. }
  1402. // read backtrack coverage tables
  1403. GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg];
  1404. for (int i = 0; i < nbg; i++) {
  1405. int bgco = bgcoa [ i ];
  1406. GlyphCoverageTable bgct;
  1407. if (bgco > 0) {
  1408. bgct = readCoverageTable(tableTag + " chained contextual substitution backtrack coverage[" + i + "]", subtableOffset + bgco);
  1409. } else {
  1410. bgct = null;
  1411. }
  1412. bgca[i] = bgct;
  1413. }
  1414. // read input coverage tables
  1415. GlyphCoverageTable[] igca = new GlyphCoverageTable[nig];
  1416. for (int i = 0; i < nig; i++) {
  1417. int igco = igcoa [ i ];
  1418. GlyphCoverageTable igct;
  1419. if (igco > 0) {
  1420. igct = readCoverageTable(tableTag + " chained contextual substitution input coverage[" + i + "]", subtableOffset + igco);
  1421. } else {
  1422. igct = null;
  1423. }
  1424. igca[i] = igct;
  1425. }
  1426. // read lookahead coverage tables
  1427. GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg];
  1428. for (int i = 0; i < nlg; i++) {
  1429. int lgco = lgcoa [ i ];
  1430. GlyphCoverageTable lgct;
  1431. if (lgco > 0) {
  1432. lgct = readCoverageTable(tableTag + " chained contextual substitution lookahead coverage[" + i + "]", subtableOffset + lgco);
  1433. } else {
  1434. lgct = null;
  1435. }
  1436. lgca[i] = lgct;
  1437. }
  1438. // read rule lookups
  1439. String header = null;
  1440. if (log.isDebugEnabled()) {
  1441. header = tableTag + " chained contextual substitution lookups: ";
  1442. }
  1443. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  1444. // construct rule, rule set, and rule set array
  1445. GlyphTable.Rule r = new GlyphTable.ChainedCoverageSequenceRule(lookups, nig, igca, bgca, lgca);
  1446. GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r});
  1447. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs};
  1448. // store results
  1449. assert (igca != null) && (igca.length > 0);
  1450. seMapping = igca[0];
  1451. seEntries.add(rsa);
  1452. }
  1453. private int readChainedContextualSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  1454. in.seekSet(subtableOffset);
  1455. // read substitution subtable format
  1456. int sf = in.readTTFUShort();
  1457. if (sf == 1) {
  1458. readChainedContextualSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  1459. } else if (sf == 2) {
  1460. readChainedContextualSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  1461. } else if (sf == 3) {
  1462. readChainedContextualSubTableFormat3(lookupType, lookupFlags, subtableOffset, sf);
  1463. } else {
  1464. throw new AdvancedTypographicTableFormatException("unsupported chained contextual substitution subtable format: " + sf);
  1465. }
  1466. return sf;
  1467. }
  1468. private void readExtensionSubTableFormat1(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset, int subtableFormat) throws IOException {
  1469. String tableTag = "GSUB";
  1470. in.seekSet(subtableOffset);
  1471. // skip over format (already known)
  1472. in.skip(2);
  1473. // read extension lookup type
  1474. int lt = in.readTTFUShort();
  1475. // read extension offset
  1476. long eo = in.readTTFULong();
  1477. // dump info if debugging
  1478. if (log.isDebugEnabled()) {
  1479. log.debug(tableTag + " extension substitution subtable format: " + subtableFormat);
  1480. log.debug(tableTag + " extension substitution lookup type: " + lt);
  1481. log.debug(tableTag + " extension substitution lookup table offset: " + eo);
  1482. }
  1483. // read referenced subtable from extended offset
  1484. readGSUBSubtable(lt, lookupFlags, lookupSequence, subtableSequence, subtableOffset + eo);
  1485. }
  1486. private int readExtensionSubTable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
  1487. in.seekSet(subtableOffset);
  1488. // read substitution subtable format
  1489. int sf = in.readTTFUShort();
  1490. if (sf == 1) {
  1491. readExtensionSubTableFormat1(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset, sf);
  1492. } else {
  1493. throw new AdvancedTypographicTableFormatException("unsupported extension substitution subtable format: " + sf);
  1494. }
  1495. return sf;
  1496. }
  1497. private void readReverseChainedSingleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1498. String tableTag = "GSUB";
  1499. in.seekSet(subtableOffset);
  1500. // skip over format (already known)
  1501. in.skip(2);
  1502. // read coverage offset
  1503. int co = in.readTTFUShort();
  1504. // read backtrack glyph count
  1505. int nbg = in.readTTFUShort();
  1506. // read backtrack glyph coverage offsets
  1507. int[] bgcoa = new int [ nbg ];
  1508. for (int i = 0; i < nbg; i++) {
  1509. bgcoa [ i ] = in.readTTFUShort();
  1510. }
  1511. // read lookahead glyph count
  1512. int nlg = in.readTTFUShort();
  1513. // read backtrack glyph coverage offsets
  1514. int[] lgcoa = new int [ nlg ];
  1515. for (int i = 0; i < nlg; i++) {
  1516. lgcoa [ i ] = in.readTTFUShort();
  1517. }
  1518. // read substitution (output) glyph count
  1519. int ng = in.readTTFUShort();
  1520. // read substitution (output) glyphs
  1521. int[] glyphs = new int [ ng ];
  1522. for (int i = 0, n = ng; i < n; i++) {
  1523. glyphs [ i ] = in.readTTFUShort();
  1524. }
  1525. // dump info if debugging
  1526. if (log.isDebugEnabled()) {
  1527. log.debug(tableTag + " reverse chained contextual substitution format: " + subtableFormat);
  1528. log.debug(tableTag + " reverse chained contextual substitution coverage table offset: " + co);
  1529. log.debug(tableTag + " reverse chained contextual substitution backtrack glyph count: " + nbg);
  1530. for (int i = 0; i < nbg; i++) {
  1531. log.debug(tableTag + " reverse chained contextual substitution backtrack coverage table offset[" + i + "]: " + bgcoa[i]);
  1532. }
  1533. log.debug(tableTag + " reverse chained contextual substitution lookahead glyph count: " + nlg);
  1534. for (int i = 0; i < nlg; i++) {
  1535. log.debug(tableTag + " reverse chained contextual substitution lookahead coverage table offset[" + i + "]: " + lgcoa[i]);
  1536. }
  1537. log.debug(tableTag + " reverse chained contextual substitution glyphs: " + toString(glyphs));
  1538. }
  1539. // read coverage table
  1540. GlyphCoverageTable ct = readCoverageTable(tableTag + " reverse chained contextual substitution coverage", subtableOffset + co);
  1541. // read backtrack coverage tables
  1542. GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg];
  1543. for (int i = 0; i < nbg; i++) {
  1544. int bgco = bgcoa[i];
  1545. GlyphCoverageTable bgct;
  1546. if (bgco > 0) {
  1547. bgct = readCoverageTable(tableTag + " reverse chained contextual substitution backtrack coverage[" + i + "]", subtableOffset + bgco);
  1548. } else {
  1549. bgct = null;
  1550. }
  1551. bgca[i] = bgct;
  1552. }
  1553. // read lookahead coverage tables
  1554. GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg];
  1555. for (int i = 0; i < nlg; i++) {
  1556. int lgco = lgcoa[i];
  1557. GlyphCoverageTable lgct;
  1558. if (lgco > 0) {
  1559. lgct = readCoverageTable(tableTag + " reverse chained contextual substitution lookahead coverage[" + i + "]", subtableOffset + lgco);
  1560. } else {
  1561. lgct = null;
  1562. }
  1563. lgca[i] = lgct;
  1564. }
  1565. // store results
  1566. seMapping = ct;
  1567. seEntries.add(bgca);
  1568. seEntries.add(lgca);
  1569. seEntries.add(glyphs);
  1570. }
  1571. private int readReverseChainedSingleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  1572. in.seekSet(subtableOffset);
  1573. // read substitution subtable format
  1574. int sf = in.readTTFUShort();
  1575. if (sf == 1) {
  1576. readReverseChainedSingleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  1577. } else {
  1578. throw new AdvancedTypographicTableFormatException("unsupported reverse chained single substitution subtable format: " + sf);
  1579. }
  1580. return sf;
  1581. }
  1582. private void readGSUBSubtable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
  1583. initATSubState();
  1584. int subtableFormat = -1;
  1585. switch (lookupType) {
  1586. case GSUBLookupType.SINGLE:
  1587. subtableFormat = readSingleSubTable(lookupType, lookupFlags, subtableOffset);
  1588. break;
  1589. case GSUBLookupType.MULTIPLE:
  1590. subtableFormat = readMultipleSubTable(lookupType, lookupFlags, subtableOffset);
  1591. break;
  1592. case GSUBLookupType.ALTERNATE:
  1593. subtableFormat = readAlternateSubTable(lookupType, lookupFlags, subtableOffset);
  1594. break;
  1595. case GSUBLookupType.LIGATURE:
  1596. subtableFormat = readLigatureSubTable(lookupType, lookupFlags, subtableOffset);
  1597. break;
  1598. case GSUBLookupType.CONTEXTUAL:
  1599. subtableFormat = readContextualSubTable(lookupType, lookupFlags, subtableOffset);
  1600. break;
  1601. case GSUBLookupType.CHAINED_CONTEXTUAL:
  1602. subtableFormat = readChainedContextualSubTable(lookupType, lookupFlags, subtableOffset);
  1603. break;
  1604. case GSUBLookupType.REVERSE_CHAINED_SINGLE:
  1605. subtableFormat = readReverseChainedSingleSubTable(lookupType, lookupFlags, subtableOffset);
  1606. break;
  1607. case GSUBLookupType.EXTENSION:
  1608. subtableFormat = readExtensionSubTable(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset);
  1609. break;
  1610. default:
  1611. break;
  1612. }
  1613. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat);
  1614. resetATSubState();
  1615. }
  1616. private GlyphPositioningTable.DeviceTable readPosDeviceTable(long subtableOffset, long deviceTableOffset) throws IOException {
  1617. long cp = in.getCurrentPos();
  1618. in.seekSet(subtableOffset + deviceTableOffset);
  1619. // read start size
  1620. int ss = in.readTTFUShort();
  1621. // read end size
  1622. int es = in.readTTFUShort();
  1623. // read delta format
  1624. int df = in.readTTFUShort();
  1625. int s1;
  1626. int m1;
  1627. int dm;
  1628. int dd;
  1629. int s2;
  1630. if (df == 1) {
  1631. s1 = 14;
  1632. m1 = 0x3;
  1633. dm = 1;
  1634. dd = 4;
  1635. s2 = 2;
  1636. } else if (df == 2) {
  1637. s1 = 12;
  1638. m1 = 0xF;
  1639. dm = 7;
  1640. dd = 16;
  1641. s2 = 4;
  1642. } else if (df == 3) {
  1643. s1 = 8;
  1644. m1 = 0xFF;
  1645. dm = 127;
  1646. dd = 256;
  1647. s2 = 8;
  1648. } else {
  1649. log.debug("unsupported device table delta format: " + df + ", ignoring device table");
  1650. return null;
  1651. }
  1652. // read deltas
  1653. int n = (es - ss) + 1;
  1654. if (n < 0) {
  1655. log.debug("invalid device table delta count: " + n + ", ignoring device table");
  1656. return null;
  1657. }
  1658. int[] da = new int [ n ];
  1659. for (int i = 0; (i < n) && (s2 > 0);) {
  1660. int p = in.readTTFUShort();
  1661. for (int j = 0, k = 16 / s2; j < k; j++) {
  1662. int d = (p >> s1) & m1;
  1663. if (d > dm) {
  1664. d -= dd;
  1665. }
  1666. if (i < n) {
  1667. da [ i++ ] = d;
  1668. } else {
  1669. break;
  1670. }
  1671. p <<= s2;
  1672. }
  1673. }
  1674. in.seekSet(cp);
  1675. return new GlyphPositioningTable.DeviceTable(ss, es, da);
  1676. }
  1677. private GlyphPositioningTable.Value readPosValue(long subtableOffset, int valueFormat) throws IOException {
  1678. // XPlacement
  1679. int xp;
  1680. if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT) != 0) {
  1681. xp = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1682. } else {
  1683. xp = 0;
  1684. }
  1685. // YPlacement
  1686. int yp;
  1687. if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT) != 0) {
  1688. yp = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1689. } else {
  1690. yp = 0;
  1691. }
  1692. // XAdvance
  1693. int xa;
  1694. if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE) != 0) {
  1695. xa = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1696. } else {
  1697. xa = 0;
  1698. }
  1699. // YAdvance
  1700. int ya;
  1701. if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE) != 0) {
  1702. ya = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1703. } else {
  1704. ya = 0;
  1705. }
  1706. // XPlaDevice
  1707. GlyphPositioningTable.DeviceTable xpd;
  1708. if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT_DEVICE) != 0) {
  1709. int xpdo = in.readTTFUShort();
  1710. xpd = readPosDeviceTable(subtableOffset, xpdo);
  1711. } else {
  1712. xpd = null;
  1713. }
  1714. // YPlaDevice
  1715. GlyphPositioningTable.DeviceTable ypd;
  1716. if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT_DEVICE) != 0) {
  1717. int ypdo = in.readTTFUShort();
  1718. ypd = readPosDeviceTable(subtableOffset, ypdo);
  1719. } else {
  1720. ypd = null;
  1721. }
  1722. // XAdvDevice
  1723. GlyphPositioningTable.DeviceTable xad;
  1724. if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE_DEVICE) != 0) {
  1725. int xado = in.readTTFUShort();
  1726. xad = readPosDeviceTable(subtableOffset, xado);
  1727. } else {
  1728. xad = null;
  1729. }
  1730. // YAdvDevice
  1731. GlyphPositioningTable.DeviceTable yad;
  1732. if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE_DEVICE) != 0) {
  1733. int yado = in.readTTFUShort();
  1734. yad = readPosDeviceTable(subtableOffset, yado);
  1735. } else {
  1736. yad = null;
  1737. }
  1738. return new GlyphPositioningTable.Value(xp, yp, xa, ya, xpd, ypd, xad, yad);
  1739. }
  1740. private void readSinglePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1741. String tableTag = "GPOS";
  1742. in.seekSet(subtableOffset);
  1743. // skip over format (already known)
  1744. in.skip(2);
  1745. // read coverage offset
  1746. int co = in.readTTFUShort();
  1747. // read value format
  1748. int vf = in.readTTFUShort();
  1749. // read value
  1750. GlyphPositioningTable.Value v = readPosValue(subtableOffset, vf);
  1751. // dump info if debugging
  1752. if (log.isDebugEnabled()) {
  1753. log.debug(tableTag + " single positioning subtable format: " + subtableFormat + " (delta)");
  1754. log.debug(tableTag + " single positioning coverage table offset: " + co);
  1755. log.debug(tableTag + " single positioning value: " + v);
  1756. }
  1757. // read coverage table
  1758. GlyphCoverageTable ct = readCoverageTable(tableTag + " single positioning coverage", subtableOffset + co);
  1759. // store results
  1760. seMapping = ct;
  1761. seEntries.add(v);
  1762. }
  1763. private void readSinglePosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1764. String tableTag = "GPOS";
  1765. in.seekSet(subtableOffset);
  1766. // skip over format (already known)
  1767. in.skip(2);
  1768. // read coverage offset
  1769. int co = in.readTTFUShort();
  1770. // read value format
  1771. int vf = in.readTTFUShort();
  1772. // read value count
  1773. int nv = in.readTTFUShort();
  1774. // dump info if debugging
  1775. if (log.isDebugEnabled()) {
  1776. log.debug(tableTag + " single positioning subtable format: " + subtableFormat + " (mapped)");
  1777. log.debug(tableTag + " single positioning coverage table offset: " + co);
  1778. log.debug(tableTag + " single positioning value count: " + nv);
  1779. }
  1780. // read coverage table
  1781. GlyphCoverageTable ct = readCoverageTable(tableTag + " single positioning coverage", subtableOffset + co);
  1782. // read positioning values
  1783. GlyphPositioningTable.Value[] pva = new GlyphPositioningTable.Value[nv];
  1784. for (int i = 0, n = nv; i < n; i++) {
  1785. GlyphPositioningTable.Value pv = readPosValue(subtableOffset, vf);
  1786. if (log.isDebugEnabled()) {
  1787. log.debug(tableTag + " single positioning value[" + i + "]: " + pv);
  1788. }
  1789. pva[i] = pv;
  1790. }
  1791. // store results
  1792. seMapping = ct;
  1793. seEntries.add(pva);
  1794. }
  1795. private int readSinglePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  1796. in.seekSet(subtableOffset);
  1797. // read positionining subtable format
  1798. int sf = in.readTTFUShort();
  1799. if (sf == 1) {
  1800. readSinglePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  1801. } else if (sf == 2) {
  1802. readSinglePosTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  1803. } else {
  1804. throw new AdvancedTypographicTableFormatException("unsupported single positioning subtable format: " + sf);
  1805. }
  1806. return sf;
  1807. }
  1808. private GlyphPositioningTable.PairValues readPosPairValues(long subtableOffset, boolean hasGlyph, int vf1, int vf2) throws IOException {
  1809. // read glyph (if present)
  1810. int glyph;
  1811. if (hasGlyph) {
  1812. glyph = in.readTTFUShort();
  1813. } else {
  1814. glyph = 0;
  1815. }
  1816. // read first value (if present)
  1817. GlyphPositioningTable.Value v1;
  1818. if (vf1 != 0) {
  1819. v1 = readPosValue(subtableOffset, vf1);
  1820. } else {
  1821. v1 = null;
  1822. }
  1823. // read second value (if present)
  1824. GlyphPositioningTable.Value v2;
  1825. if (vf2 != 0) {
  1826. v2 = readPosValue(subtableOffset, vf2);
  1827. } else {
  1828. v2 = null;
  1829. }
  1830. return new GlyphPositioningTable.PairValues(glyph, v1, v2);
  1831. }
  1832. private GlyphPositioningTable.PairValues[] readPosPairSetTable(long subtableOffset, int pairSetTableOffset, int vf1, int vf2) throws IOException {
  1833. String tableTag = "GPOS";
  1834. long cp = in.getCurrentPos();
  1835. in.seekSet(subtableOffset + pairSetTableOffset);
  1836. // read pair values count
  1837. int npv = in.readTTFUShort();
  1838. // dump info if debugging
  1839. if (log.isDebugEnabled()) {
  1840. log.debug(tableTag + " pair set table offset: " + pairSetTableOffset);
  1841. log.debug(tableTag + " pair set table values count: " + npv);
  1842. }
  1843. // read pair values
  1844. GlyphPositioningTable.PairValues[] pva = new GlyphPositioningTable.PairValues [ npv ];
  1845. for (int i = 0, n = npv; i < n; i++) {
  1846. GlyphPositioningTable.PairValues pv = readPosPairValues(subtableOffset, true, vf1, vf2);
  1847. pva [ i ] = pv;
  1848. if (log.isDebugEnabled()) {
  1849. log.debug(tableTag + " pair set table value[" + i + "]: " + pv);
  1850. }
  1851. }
  1852. in.seekSet(cp);
  1853. return pva;
  1854. }
  1855. private void readPairPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1856. String tableTag = "GPOS";
  1857. in.seekSet(subtableOffset);
  1858. // skip over format (already known)
  1859. in.skip(2);
  1860. // read coverage offset
  1861. int co = in.readTTFUShort();
  1862. // read value format for first glyph
  1863. int vf1 = in.readTTFUShort();
  1864. // read value format for second glyph
  1865. int vf2 = in.readTTFUShort();
  1866. // read number (count) of pair sets
  1867. int nps = in.readTTFUShort();
  1868. // dump info if debugging
  1869. if (log.isDebugEnabled()) {
  1870. log.debug(tableTag + " pair positioning subtable format: " + subtableFormat + " (glyphs)");
  1871. log.debug(tableTag + " pair positioning coverage table offset: " + co);
  1872. log.debug(tableTag + " pair positioning value format #1: " + vf1);
  1873. log.debug(tableTag + " pair positioning value format #2: " + vf2);
  1874. }
  1875. // read coverage table
  1876. GlyphCoverageTable ct = readCoverageTable(tableTag + " pair positioning coverage", subtableOffset + co);
  1877. // read pair value matrix
  1878. GlyphPositioningTable.PairValues[][] pvm = new GlyphPositioningTable.PairValues [ nps ][];
  1879. for (int i = 0, n = nps; i < n; i++) {
  1880. // read pair set offset
  1881. int pso = in.readTTFUShort();
  1882. // read pair set table at offset
  1883. pvm [ i ] = readPosPairSetTable(subtableOffset, pso, vf1, vf2);
  1884. }
  1885. // store results
  1886. seMapping = ct;
  1887. seEntries.add(pvm);
  1888. }
  1889. private void readPairPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  1890. String tableTag = "GPOS";
  1891. in.seekSet(subtableOffset);
  1892. // skip over format (already known)
  1893. in.skip(2);
  1894. // read coverage offset
  1895. int co = in.readTTFUShort();
  1896. // read value format for first glyph
  1897. int vf1 = in.readTTFUShort();
  1898. // read value format for second glyph
  1899. int vf2 = in.readTTFUShort();
  1900. // read class def 1 offset
  1901. int cd1o = in.readTTFUShort();
  1902. // read class def 2 offset
  1903. int cd2o = in.readTTFUShort();
  1904. // read number (count) of classes in class def 1 table
  1905. int nc1 = in.readTTFUShort();
  1906. // read number (count) of classes in class def 2 table
  1907. int nc2 = in.readTTFUShort();
  1908. // dump info if debugging
  1909. if (log.isDebugEnabled()) {
  1910. log.debug(tableTag + " pair positioning subtable format: " + subtableFormat + " (glyph classes)");
  1911. log.debug(tableTag + " pair positioning coverage table offset: " + co);
  1912. log.debug(tableTag + " pair positioning value format #1: " + vf1);
  1913. log.debug(tableTag + " pair positioning value format #2: " + vf2);
  1914. log.debug(tableTag + " pair positioning class def table #1 offset: " + cd1o);
  1915. log.debug(tableTag + " pair positioning class def table #2 offset: " + cd2o);
  1916. log.debug(tableTag + " pair positioning class #1 count: " + nc1);
  1917. log.debug(tableTag + " pair positioning class #2 count: " + nc2);
  1918. }
  1919. // read coverage table
  1920. GlyphCoverageTable ct = readCoverageTable(tableTag + " pair positioning coverage", subtableOffset + co);
  1921. // read class definition table #1
  1922. GlyphClassTable cdt1 = readClassDefTable(tableTag + " pair positioning class definition #1", subtableOffset + cd1o);
  1923. // read class definition table #2
  1924. GlyphClassTable cdt2 = readClassDefTable(tableTag + " pair positioning class definition #2", subtableOffset + cd2o);
  1925. // read pair value matrix
  1926. GlyphPositioningTable.PairValues[][] pvm = new GlyphPositioningTable.PairValues [ nc1 ] [ nc2 ];
  1927. for (int i = 0; i < nc1; i++) {
  1928. for (int j = 0; j < nc2; j++) {
  1929. GlyphPositioningTable.PairValues pv = readPosPairValues(subtableOffset, false, vf1, vf2);
  1930. pvm [ i ] [ j ] = pv;
  1931. if (log.isDebugEnabled()) {
  1932. log.debug(tableTag + " pair set table value[" + i + "][" + j + "]: " + pv);
  1933. }
  1934. }
  1935. }
  1936. // store results
  1937. seMapping = ct;
  1938. seEntries.add(cdt1);
  1939. seEntries.add(cdt2);
  1940. seEntries.add(Integer.valueOf(nc1));
  1941. seEntries.add(Integer.valueOf(nc2));
  1942. seEntries.add(pvm);
  1943. }
  1944. private int readPairPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  1945. in.seekSet(subtableOffset);
  1946. // read positioning subtable format
  1947. int sf = in.readTTFUShort();
  1948. if (sf == 1) {
  1949. readPairPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  1950. } else if (sf == 2) {
  1951. readPairPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  1952. } else {
  1953. throw new AdvancedTypographicTableFormatException("unsupported pair positioning subtable format: " + sf);
  1954. }
  1955. return sf;
  1956. }
  1957. private GlyphPositioningTable.Anchor readPosAnchor(long anchorTableOffset) throws IOException {
  1958. GlyphPositioningTable.Anchor a;
  1959. long cp = in.getCurrentPos();
  1960. in.seekSet(anchorTableOffset);
  1961. // read anchor table format
  1962. int af = in.readTTFUShort();
  1963. if (af == 1) {
  1964. // read x coordinate
  1965. int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1966. // read y coordinate
  1967. int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1968. a = new GlyphPositioningTable.Anchor(x, y);
  1969. } else if (af == 2) {
  1970. // read x coordinate
  1971. int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1972. // read y coordinate
  1973. int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1974. // read anchor point index
  1975. int ap = in.readTTFUShort();
  1976. a = new GlyphPositioningTable.Anchor(x, y, ap);
  1977. } else if (af == 3) {
  1978. // read x coordinate
  1979. int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1980. // read y coordinate
  1981. int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
  1982. // read x device table offset
  1983. int xdo = in.readTTFUShort();
  1984. // read y device table offset
  1985. int ydo = in.readTTFUShort();
  1986. // read x device table (if present)
  1987. GlyphPositioningTable.DeviceTable xd;
  1988. if (xdo != 0) {
  1989. xd = readPosDeviceTable(cp, xdo);
  1990. } else {
  1991. xd = null;
  1992. }
  1993. // read y device table (if present)
  1994. GlyphPositioningTable.DeviceTable yd;
  1995. if (ydo != 0) {
  1996. yd = readPosDeviceTable(cp, ydo);
  1997. } else {
  1998. yd = null;
  1999. }
  2000. a = new GlyphPositioningTable.Anchor(x, y, xd, yd);
  2001. } else {
  2002. throw new AdvancedTypographicTableFormatException("unsupported positioning anchor format: " + af);
  2003. }
  2004. in.seekSet(cp);
  2005. return a;
  2006. }
  2007. private void readCursivePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2008. String tableTag = "GPOS";
  2009. in.seekSet(subtableOffset);
  2010. // skip over format (already known)
  2011. in.skip(2);
  2012. // read coverage offset
  2013. int co = in.readTTFUShort();
  2014. // read entry/exit count
  2015. int ec = in.readTTFUShort();
  2016. // dump info if debugging
  2017. if (log.isDebugEnabled()) {
  2018. log.debug(tableTag + " cursive positioning subtable format: " + subtableFormat);
  2019. log.debug(tableTag + " cursive positioning coverage table offset: " + co);
  2020. log.debug(tableTag + " cursive positioning entry/exit count: " + ec);
  2021. }
  2022. // read coverage table
  2023. GlyphCoverageTable ct = readCoverageTable(tableTag + " cursive positioning coverage", subtableOffset + co);
  2024. // read entry/exit records
  2025. GlyphPositioningTable.Anchor[] aa = new GlyphPositioningTable.Anchor [ ec * 2 ];
  2026. for (int i = 0, n = ec; i < n; i++) {
  2027. // read entry anchor offset
  2028. int eno = in.readTTFUShort();
  2029. // read exit anchor offset
  2030. int exo = in.readTTFUShort();
  2031. // read entry anchor
  2032. GlyphPositioningTable.Anchor ena;
  2033. if (eno > 0) {
  2034. ena = readPosAnchor(subtableOffset + eno);
  2035. } else {
  2036. ena = null;
  2037. }
  2038. // read exit anchor
  2039. GlyphPositioningTable.Anchor exa;
  2040. if (exo > 0) {
  2041. exa = readPosAnchor(subtableOffset + exo);
  2042. } else {
  2043. exa = null;
  2044. }
  2045. aa [ (i * 2) + 0 ] = ena;
  2046. aa [ (i * 2) + 1 ] = exa;
  2047. if (log.isDebugEnabled()) {
  2048. if (ena != null) {
  2049. log.debug(tableTag + " cursive entry anchor [" + i + "]: " + ena);
  2050. }
  2051. if (exa != null) {
  2052. log.debug(tableTag + " cursive exit anchor [" + i + "]: " + exa);
  2053. }
  2054. }
  2055. }
  2056. // store results
  2057. seMapping = ct;
  2058. seEntries.add(aa);
  2059. }
  2060. private int readCursivePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2061. in.seekSet(subtableOffset);
  2062. // read positioning subtable format
  2063. int sf = in.readTTFUShort();
  2064. if (sf == 1) {
  2065. readCursivePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2066. } else {
  2067. throw new AdvancedTypographicTableFormatException("unsupported cursive positioning subtable format: " + sf);
  2068. }
  2069. return sf;
  2070. }
  2071. private void readMarkToBasePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2072. String tableTag = "GPOS";
  2073. in.seekSet(subtableOffset);
  2074. // skip over format (already known)
  2075. in.skip(2);
  2076. // read mark coverage offset
  2077. int mco = in.readTTFUShort();
  2078. // read base coverage offset
  2079. int bco = in.readTTFUShort();
  2080. // read mark class count
  2081. int nmc = in.readTTFUShort();
  2082. // read mark array offset
  2083. int mao = in.readTTFUShort();
  2084. // read base array offset
  2085. int bao = in.readTTFUShort();
  2086. // dump info if debugging
  2087. if (log.isDebugEnabled()) {
  2088. log.debug(tableTag + " mark-to-base positioning subtable format: " + subtableFormat);
  2089. log.debug(tableTag + " mark-to-base positioning mark coverage table offset: " + mco);
  2090. log.debug(tableTag + " mark-to-base positioning base coverage table offset: " + bco);
  2091. log.debug(tableTag + " mark-to-base positioning mark class count: " + nmc);
  2092. log.debug(tableTag + " mark-to-base positioning mark array offset: " + mao);
  2093. log.debug(tableTag + " mark-to-base positioning base array offset: " + bao);
  2094. }
  2095. // read mark coverage table
  2096. GlyphCoverageTable mct = readCoverageTable(tableTag + " mark-to-base positioning mark coverage", subtableOffset + mco);
  2097. // read base coverage table
  2098. GlyphCoverageTable bct = readCoverageTable(tableTag + " mark-to-base positioning base coverage", subtableOffset + bco);
  2099. // read mark anchor array
  2100. // seek to mark array
  2101. in.seekSet(subtableOffset + mao);
  2102. // read mark count
  2103. int nm = in.readTTFUShort();
  2104. if (log.isDebugEnabled()) {
  2105. log.debug(tableTag + " mark-to-base positioning mark count: " + nm);
  2106. }
  2107. // read mark anchor array, where i:{0...markCount}
  2108. GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm ];
  2109. for (int i = 0; i < nm; i++) {
  2110. // read mark class
  2111. int mc = in.readTTFUShort();
  2112. // read mark anchor offset
  2113. int ao = in.readTTFUShort();
  2114. GlyphPositioningTable.Anchor a;
  2115. if (ao > 0) {
  2116. a = readPosAnchor(subtableOffset + mao + ao);
  2117. } else {
  2118. a = null;
  2119. }
  2120. GlyphPositioningTable.MarkAnchor ma;
  2121. if (a != null) {
  2122. ma = new GlyphPositioningTable.MarkAnchor(mc, a);
  2123. } else {
  2124. ma = null;
  2125. }
  2126. maa [ i ] = ma;
  2127. if (log.isDebugEnabled()) {
  2128. log.debug(tableTag + " mark-to-base positioning mark anchor[" + i + "]: " + ma);
  2129. }
  2130. }
  2131. // read base anchor matrix
  2132. // seek to base array
  2133. in.seekSet(subtableOffset + bao);
  2134. // read base count
  2135. int nb = in.readTTFUShort();
  2136. if (log.isDebugEnabled()) {
  2137. log.debug(tableTag + " mark-to-base positioning base count: " + nb);
  2138. }
  2139. // read anchor matrix, where i:{0...baseCount - 1}, j:{0...markClassCount - 1}
  2140. GlyphPositioningTable.Anchor[][] bam = new GlyphPositioningTable.Anchor [ nb ] [ nmc ];
  2141. for (int i = 0; i < nb; i++) {
  2142. for (int j = 0; j < nmc; j++) {
  2143. // read base anchor offset
  2144. int ao = in.readTTFUShort();
  2145. GlyphPositioningTable.Anchor a;
  2146. if (ao > 0) {
  2147. a = readPosAnchor(subtableOffset + bao + ao);
  2148. } else {
  2149. a = null;
  2150. }
  2151. bam [ i ] [ j ] = a;
  2152. if (log.isDebugEnabled()) {
  2153. log.debug(tableTag + " mark-to-base positioning base anchor[" + i + "][" + j + "]: " + a);
  2154. }
  2155. }
  2156. }
  2157. // store results
  2158. seMapping = mct;
  2159. seEntries.add(bct);
  2160. seEntries.add(Integer.valueOf(nmc));
  2161. seEntries.add(maa);
  2162. seEntries.add(bam);
  2163. }
  2164. private int readMarkToBasePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2165. in.seekSet(subtableOffset);
  2166. // read positioning subtable format
  2167. int sf = in.readTTFUShort();
  2168. if (sf == 1) {
  2169. readMarkToBasePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2170. } else {
  2171. throw new AdvancedTypographicTableFormatException("unsupported mark-to-base positioning subtable format: " + sf);
  2172. }
  2173. return sf;
  2174. }
  2175. private void readMarkToLigaturePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2176. String tableTag = "GPOS";
  2177. in.seekSet(subtableOffset);
  2178. // skip over format (already known)
  2179. in.skip(2);
  2180. // read mark coverage offset
  2181. int mco = in.readTTFUShort();
  2182. // read ligature coverage offset
  2183. int lco = in.readTTFUShort();
  2184. // read mark class count
  2185. int nmc = in.readTTFUShort();
  2186. // read mark array offset
  2187. int mao = in.readTTFUShort();
  2188. // read ligature array offset
  2189. int lao = in.readTTFUShort();
  2190. // dump info if debugging
  2191. if (log.isDebugEnabled()) {
  2192. log.debug(tableTag + " mark-to-ligature positioning subtable format: " + subtableFormat);
  2193. log.debug(tableTag + " mark-to-ligature positioning mark coverage table offset: " + mco);
  2194. log.debug(tableTag + " mark-to-ligature positioning ligature coverage table offset: " + lco);
  2195. log.debug(tableTag + " mark-to-ligature positioning mark class count: " + nmc);
  2196. log.debug(tableTag + " mark-to-ligature positioning mark array offset: " + mao);
  2197. log.debug(tableTag + " mark-to-ligature positioning ligature array offset: " + lao);
  2198. }
  2199. // read mark coverage table
  2200. GlyphCoverageTable mct = readCoverageTable(tableTag + " mark-to-ligature positioning mark coverage", subtableOffset + mco);
  2201. // read ligature coverage table
  2202. GlyphCoverageTable lct = readCoverageTable(tableTag + " mark-to-ligature positioning ligature coverage", subtableOffset + lco);
  2203. // read mark anchor array
  2204. // seek to mark array
  2205. in.seekSet(subtableOffset + mao);
  2206. // read mark count
  2207. int nm = in.readTTFUShort();
  2208. if (log.isDebugEnabled()) {
  2209. log.debug(tableTag + " mark-to-ligature positioning mark count: " + nm);
  2210. }
  2211. // read mark anchor array, where i:{0...markCount}
  2212. GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm ];
  2213. for (int i = 0; i < nm; i++) {
  2214. // read mark class
  2215. int mc = in.readTTFUShort();
  2216. // read mark anchor offset
  2217. int ao = in.readTTFUShort();
  2218. GlyphPositioningTable.Anchor a;
  2219. if (ao > 0) {
  2220. a = readPosAnchor(subtableOffset + mao + ao);
  2221. } else {
  2222. a = null;
  2223. }
  2224. GlyphPositioningTable.MarkAnchor ma;
  2225. if (a != null) {
  2226. ma = new GlyphPositioningTable.MarkAnchor(mc, a);
  2227. } else {
  2228. ma = null;
  2229. }
  2230. maa [ i ] = ma;
  2231. if (log.isDebugEnabled()) {
  2232. log.debug(tableTag + " mark-to-ligature positioning mark anchor[" + i + "]: " + ma);
  2233. }
  2234. }
  2235. // read ligature anchor matrix
  2236. // seek to ligature array
  2237. in.seekSet(subtableOffset + lao);
  2238. // read ligature count
  2239. int nl = in.readTTFUShort();
  2240. if (log.isDebugEnabled()) {
  2241. log.debug(tableTag + " mark-to-ligature positioning ligature count: " + nl);
  2242. }
  2243. // read ligature attach table offsets
  2244. int[] laoa = new int [ nl ];
  2245. for (int i = 0; i < nl; i++) {
  2246. laoa [ i ] = in.readTTFUShort();
  2247. }
  2248. // iterate over ligature attach tables, recording maximum component count
  2249. int mxc = 0;
  2250. for (int i = 0; i < nl; i++) {
  2251. int lato = laoa [ i ];
  2252. in.seekSet(subtableOffset + lao + lato);
  2253. // read component count
  2254. int cc = in.readTTFUShort();
  2255. if (cc > mxc) {
  2256. mxc = cc;
  2257. }
  2258. }
  2259. if (log.isDebugEnabled()) {
  2260. log.debug(tableTag + " mark-to-ligature positioning maximum component count: " + mxc);
  2261. }
  2262. // read anchor matrix, where i:{0...ligatureCount - 1}, j:{0...maxComponentCount - 1}, k:{0...markClassCount - 1}
  2263. GlyphPositioningTable.Anchor[][][] lam = new GlyphPositioningTable.Anchor [ nl ][][];
  2264. for (int i = 0; i < nl; i++) {
  2265. int lato = laoa [ i ];
  2266. // seek to ligature attach table for ligature[i]
  2267. in.seekSet(subtableOffset + lao + lato);
  2268. // read component count
  2269. int cc = in.readTTFUShort();
  2270. GlyphPositioningTable.Anchor[][] lcm = new GlyphPositioningTable.Anchor [ cc ] [ nmc ];
  2271. for (int j = 0; j < cc; j++) {
  2272. for (int k = 0; k < nmc; k++) {
  2273. // read ligature anchor offset
  2274. int ao = in.readTTFUShort();
  2275. GlyphPositioningTable.Anchor a;
  2276. if (ao > 0) {
  2277. a = readPosAnchor(subtableOffset + lao + lato + ao);
  2278. } else {
  2279. a = null;
  2280. }
  2281. lcm [ j ] [ k ] = a;
  2282. if (log.isDebugEnabled()) {
  2283. log.debug(tableTag + " mark-to-ligature positioning ligature anchor[" + i + "][" + j + "][" + k + "]: " + a);
  2284. }
  2285. }
  2286. }
  2287. lam [ i ] = lcm;
  2288. }
  2289. // store results
  2290. seMapping = mct;
  2291. seEntries.add(lct);
  2292. seEntries.add(Integer.valueOf(nmc));
  2293. seEntries.add(Integer.valueOf(mxc));
  2294. seEntries.add(maa);
  2295. seEntries.add(lam);
  2296. }
  2297. private int readMarkToLigaturePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2298. in.seekSet(subtableOffset);
  2299. // read positioning subtable format
  2300. int sf = in.readTTFUShort();
  2301. if (sf == 1) {
  2302. readMarkToLigaturePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2303. } else {
  2304. throw new AdvancedTypographicTableFormatException("unsupported mark-to-ligature positioning subtable format: " + sf);
  2305. }
  2306. return sf;
  2307. }
  2308. private void readMarkToMarkPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2309. String tableTag = "GPOS";
  2310. in.seekSet(subtableOffset);
  2311. // skip over format (already known)
  2312. in.skip(2);
  2313. // read mark #1 coverage offset
  2314. int m1co = in.readTTFUShort();
  2315. // read mark #2 coverage offset
  2316. int m2co = in.readTTFUShort();
  2317. // read mark class count
  2318. int nmc = in.readTTFUShort();
  2319. // read mark #1 array offset
  2320. int m1ao = in.readTTFUShort();
  2321. // read mark #2 array offset
  2322. int m2ao = in.readTTFUShort();
  2323. // dump info if debugging
  2324. if (log.isDebugEnabled()) {
  2325. log.debug(tableTag + " mark-to-mark positioning subtable format: " + subtableFormat);
  2326. log.debug(tableTag + " mark-to-mark positioning mark #1 coverage table offset: " + m1co);
  2327. log.debug(tableTag + " mark-to-mark positioning mark #2 coverage table offset: " + m2co);
  2328. log.debug(tableTag + " mark-to-mark positioning mark class count: " + nmc);
  2329. log.debug(tableTag + " mark-to-mark positioning mark #1 array offset: " + m1ao);
  2330. log.debug(tableTag + " mark-to-mark positioning mark #2 array offset: " + m2ao);
  2331. }
  2332. // read mark #1 coverage table
  2333. GlyphCoverageTable mct1 = readCoverageTable(tableTag + " mark-to-mark positioning mark #1 coverage", subtableOffset + m1co);
  2334. // read mark #2 coverage table
  2335. GlyphCoverageTable mct2 = readCoverageTable(tableTag + " mark-to-mark positioning mark #2 coverage", subtableOffset + m2co);
  2336. // read mark #1 anchor array
  2337. // seek to mark array
  2338. in.seekSet(subtableOffset + m1ao);
  2339. // read mark count
  2340. int nm1 = in.readTTFUShort();
  2341. if (log.isDebugEnabled()) {
  2342. log.debug(tableTag + " mark-to-mark positioning mark #1 count: " + nm1);
  2343. }
  2344. // read mark anchor array, where i:{0...mark1Count}
  2345. GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm1 ];
  2346. for (int i = 0; i < nm1; i++) {
  2347. // read mark class
  2348. int mc = in.readTTFUShort();
  2349. // read mark anchor offset
  2350. int ao = in.readTTFUShort();
  2351. GlyphPositioningTable.Anchor a;
  2352. if (ao > 0) {
  2353. a = readPosAnchor(subtableOffset + m1ao + ao);
  2354. } else {
  2355. a = null;
  2356. }
  2357. GlyphPositioningTable.MarkAnchor ma;
  2358. if (a != null) {
  2359. ma = new GlyphPositioningTable.MarkAnchor(mc, a);
  2360. } else {
  2361. ma = null;
  2362. }
  2363. maa [ i ] = ma;
  2364. if (log.isDebugEnabled()) {
  2365. log.debug(tableTag + " mark-to-mark positioning mark #1 anchor[" + i + "]: " + ma);
  2366. }
  2367. }
  2368. // read mark #2 anchor matrix
  2369. // seek to mark #2 array
  2370. in.seekSet(subtableOffset + m2ao);
  2371. // read mark #2 count
  2372. int nm2 = in.readTTFUShort();
  2373. if (log.isDebugEnabled()) {
  2374. log.debug(tableTag + " mark-to-mark positioning mark #2 count: " + nm2);
  2375. }
  2376. // read anchor matrix, where i:{0...mark2Count - 1}, j:{0...markClassCount - 1}
  2377. GlyphPositioningTable.Anchor[][] mam = new GlyphPositioningTable.Anchor [ nm2 ] [ nmc ];
  2378. for (int i = 0; i < nm2; i++) {
  2379. for (int j = 0; j < nmc; j++) {
  2380. // read mark anchor offset
  2381. int ao = in.readTTFUShort();
  2382. GlyphPositioningTable.Anchor a;
  2383. if (ao > 0) {
  2384. a = readPosAnchor(subtableOffset + m2ao + ao);
  2385. } else {
  2386. a = null;
  2387. }
  2388. mam [ i ] [ j ] = a;
  2389. if (log.isDebugEnabled()) {
  2390. log.debug(tableTag + " mark-to-mark positioning mark #2 anchor[" + i + "][" + j + "]: " + a);
  2391. }
  2392. }
  2393. }
  2394. // store results
  2395. seMapping = mct1;
  2396. seEntries.add(mct2);
  2397. seEntries.add(Integer.valueOf(nmc));
  2398. seEntries.add(maa);
  2399. seEntries.add(mam);
  2400. }
  2401. private int readMarkToMarkPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2402. in.seekSet(subtableOffset);
  2403. // read positioning subtable format
  2404. int sf = in.readTTFUShort();
  2405. if (sf == 1) {
  2406. readMarkToMarkPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2407. } else {
  2408. throw new AdvancedTypographicTableFormatException("unsupported mark-to-mark positioning subtable format: " + sf);
  2409. }
  2410. return sf;
  2411. }
  2412. private void readContextualPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2413. String tableTag = "GPOS";
  2414. in.seekSet(subtableOffset);
  2415. // skip over format (already known)
  2416. in.skip(2);
  2417. // read coverage offset
  2418. int co = in.readTTFUShort();
  2419. // read rule set count
  2420. int nrs = in.readTTFUShort();
  2421. // read rule set offsets
  2422. int[] rsoa = new int [ nrs ];
  2423. for (int i = 0; i < nrs; i++) {
  2424. rsoa [ i ] = in.readTTFUShort();
  2425. }
  2426. // dump info if debugging
  2427. if (log.isDebugEnabled()) {
  2428. log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyphs)");
  2429. log.debug(tableTag + " contextual positioning coverage table offset: " + co);
  2430. log.debug(tableTag + " contextual positioning rule set count: " + nrs);
  2431. for (int i = 0; i < nrs; i++) {
  2432. log.debug(tableTag + " contextual positioning rule set offset[" + i + "]: " + rsoa[i]);
  2433. }
  2434. }
  2435. // read coverage table
  2436. GlyphCoverageTable ct;
  2437. if (co > 0) {
  2438. ct = readCoverageTable(tableTag + " contextual positioning coverage", subtableOffset + co);
  2439. } else {
  2440. ct = null;
  2441. }
  2442. // read rule sets
  2443. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ];
  2444. String header = null;
  2445. for (int i = 0; i < nrs; i++) {
  2446. GlyphTable.RuleSet rs;
  2447. int rso = rsoa [ i ];
  2448. if (rso > 0) {
  2449. // seek to rule set [ i ]
  2450. in.seekSet(subtableOffset + rso);
  2451. // read rule count
  2452. int nr = in.readTTFUShort();
  2453. // read rule offsets
  2454. int[] roa = new int [ nr ];
  2455. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  2456. for (int j = 0; j < nr; j++) {
  2457. roa [ j ] = in.readTTFUShort();
  2458. }
  2459. // read glyph sequence rules
  2460. for (int j = 0; j < nr; j++) {
  2461. GlyphTable.GlyphSequenceRule r;
  2462. int ro = roa [ j ];
  2463. if (ro > 0) {
  2464. // seek to rule [ j ]
  2465. in.seekSet(subtableOffset + rso + ro);
  2466. // read glyph count
  2467. int ng = in.readTTFUShort();
  2468. // read rule lookup count
  2469. int nl = in.readTTFUShort();
  2470. // read glyphs
  2471. int[] glyphs = new int [ ng - 1 ];
  2472. for (int k = 0, nk = glyphs.length; k < nk; k++) {
  2473. glyphs [ k ] = in.readTTFUShort();
  2474. }
  2475. // read rule lookups
  2476. if (log.isDebugEnabled()) {
  2477. header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: ";
  2478. }
  2479. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2480. r = new GlyphTable.GlyphSequenceRule(lookups, ng, glyphs);
  2481. } else {
  2482. r = null;
  2483. }
  2484. ra [ j ] = r;
  2485. }
  2486. rs = new GlyphTable.HomogeneousRuleSet(ra);
  2487. } else {
  2488. rs = null;
  2489. }
  2490. rsa [ i ] = rs;
  2491. }
  2492. // store results
  2493. seMapping = ct;
  2494. seEntries.add(rsa);
  2495. }
  2496. private void readContextualPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2497. String tableTag = "GPOS";
  2498. in.seekSet(subtableOffset);
  2499. // skip over format (already known)
  2500. in.skip(2);
  2501. // read coverage offset
  2502. int co = in.readTTFUShort();
  2503. // read class def table offset
  2504. int cdo = in.readTTFUShort();
  2505. // read class rule set count
  2506. int ngc = in.readTTFUShort();
  2507. // read class rule set offsets
  2508. int[] csoa = new int [ ngc ];
  2509. for (int i = 0; i < ngc; i++) {
  2510. csoa [ i ] = in.readTTFUShort();
  2511. }
  2512. // dump info if debugging
  2513. if (log.isDebugEnabled()) {
  2514. log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyph classes)");
  2515. log.debug(tableTag + " contextual positioning coverage table offset: " + co);
  2516. log.debug(tableTag + " contextual positioning class set count: " + ngc);
  2517. for (int i = 0; i < ngc; i++) {
  2518. log.debug(tableTag + " contextual positioning class set offset[" + i + "]: " + csoa[i]);
  2519. }
  2520. }
  2521. // read coverage table
  2522. GlyphCoverageTable ct;
  2523. if (co > 0) {
  2524. ct = readCoverageTable(tableTag + " contextual positioning coverage", subtableOffset + co);
  2525. } else {
  2526. ct = null;
  2527. }
  2528. // read class definition table
  2529. GlyphClassTable cdt;
  2530. if (cdo > 0) {
  2531. cdt = readClassDefTable(tableTag + " contextual positioning class definition", subtableOffset + cdo);
  2532. } else {
  2533. cdt = null;
  2534. }
  2535. // read rule sets
  2536. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ];
  2537. String header = null;
  2538. for (int i = 0; i < ngc; i++) {
  2539. int cso = csoa [ i ];
  2540. GlyphTable.RuleSet rs;
  2541. if (cso > 0) {
  2542. // seek to rule set [ i ]
  2543. in.seekSet(subtableOffset + cso);
  2544. // read rule count
  2545. int nr = in.readTTFUShort();
  2546. // read rule offsets
  2547. int[] roa = new int [ nr ];
  2548. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  2549. for (int j = 0; j < nr; j++) {
  2550. roa [ j ] = in.readTTFUShort();
  2551. }
  2552. // read glyph sequence rules
  2553. for (int j = 0; j < nr; j++) {
  2554. int ro = roa [ j ];
  2555. GlyphTable.ClassSequenceRule r;
  2556. if (ro > 0) {
  2557. // seek to rule [ j ]
  2558. in.seekSet(subtableOffset + cso + ro);
  2559. // read glyph count
  2560. int ng = in.readTTFUShort();
  2561. // read rule lookup count
  2562. int nl = in.readTTFUShort();
  2563. // read classes
  2564. int[] classes = new int [ ng - 1 ];
  2565. for (int k = 0, nk = classes.length; k < nk; k++) {
  2566. classes [ k ] = in.readTTFUShort();
  2567. }
  2568. // read rule lookups
  2569. if (log.isDebugEnabled()) {
  2570. header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: ";
  2571. }
  2572. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2573. r = new GlyphTable.ClassSequenceRule(lookups, ng, classes);
  2574. } else {
  2575. r = null;
  2576. }
  2577. ra [ j ] = r;
  2578. }
  2579. rs = new GlyphTable.HomogeneousRuleSet(ra);
  2580. } else {
  2581. rs = null;
  2582. }
  2583. rsa [ i ] = rs;
  2584. }
  2585. // store results
  2586. seMapping = ct;
  2587. seEntries.add(cdt);
  2588. seEntries.add(Integer.valueOf(ngc));
  2589. seEntries.add(rsa);
  2590. }
  2591. private void readContextualPosTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2592. String tableTag = "GPOS";
  2593. in.seekSet(subtableOffset);
  2594. // skip over format (already known)
  2595. in.skip(2);
  2596. // read glyph (input sequence length) count
  2597. int ng = in.readTTFUShort();
  2598. // read positioning lookup count
  2599. int nl = in.readTTFUShort();
  2600. // read glyph coverage offsets, one per glyph input sequence length count
  2601. int[] gcoa = new int [ ng ];
  2602. for (int i = 0; i < ng; i++) {
  2603. gcoa [ i ] = in.readTTFUShort();
  2604. }
  2605. // dump info if debugging
  2606. if (log.isDebugEnabled()) {
  2607. log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyph sets)");
  2608. log.debug(tableTag + " contextual positioning glyph input sequence length count: " + ng);
  2609. log.debug(tableTag + " contextual positioning lookup count: " + nl);
  2610. for (int i = 0; i < ng; i++) {
  2611. log.debug(tableTag + " contextual positioning coverage table offset[" + i + "]: " + gcoa[i]);
  2612. }
  2613. }
  2614. // read coverage tables
  2615. GlyphCoverageTable[] gca = new GlyphCoverageTable [ ng ];
  2616. for (int i = 0; i < ng; i++) {
  2617. int gco = gcoa [ i ];
  2618. GlyphCoverageTable gct;
  2619. if (gco > 0) {
  2620. gct = readCoverageTable(tableTag + " contextual positioning coverage[" + i + "]", subtableOffset + gcoa[i]);
  2621. } else {
  2622. gct = null;
  2623. }
  2624. gca [ i ] = gct;
  2625. }
  2626. // read rule lookups
  2627. String header = null;
  2628. if (log.isDebugEnabled()) {
  2629. header = tableTag + " contextual positioning lookups: ";
  2630. }
  2631. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2632. // construct rule, rule set, and rule set array
  2633. GlyphTable.Rule r = new GlyphTable.CoverageSequenceRule(lookups, ng, gca);
  2634. GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r});
  2635. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs};
  2636. // store results
  2637. assert (gca != null) && (gca.length > 0);
  2638. seMapping = gca[0];
  2639. seEntries.add(rsa);
  2640. }
  2641. private int readContextualPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2642. in.seekSet(subtableOffset);
  2643. // read positioning subtable format
  2644. int sf = in.readTTFUShort();
  2645. if (sf == 1) {
  2646. readContextualPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2647. } else if (sf == 2) {
  2648. readContextualPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  2649. } else if (sf == 3) {
  2650. readContextualPosTableFormat3(lookupType, lookupFlags, subtableOffset, sf);
  2651. } else {
  2652. throw new AdvancedTypographicTableFormatException("unsupported contextual positioning subtable format: " + sf);
  2653. }
  2654. return sf;
  2655. }
  2656. private void readChainedContextualPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2657. String tableTag = "GPOS";
  2658. in.seekSet(subtableOffset);
  2659. // skip over format (already known)
  2660. in.skip(2);
  2661. // read coverage offset
  2662. int co = in.readTTFUShort();
  2663. // read rule set count
  2664. int nrs = in.readTTFUShort();
  2665. // read rule set offsets
  2666. int[] rsoa = new int [ nrs ];
  2667. for (int i = 0; i < nrs; i++) {
  2668. rsoa [ i ] = in.readTTFUShort();
  2669. }
  2670. // dump info if debugging
  2671. if (log.isDebugEnabled()) {
  2672. log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyphs)");
  2673. log.debug(tableTag + " chained contextual positioning coverage table offset: " + co);
  2674. log.debug(tableTag + " chained contextual positioning rule set count: " + nrs);
  2675. for (int i = 0; i < nrs; i++) {
  2676. log.debug(tableTag + " chained contextual positioning rule set offset[" + i + "]: " + rsoa[i]);
  2677. }
  2678. }
  2679. // read coverage table
  2680. GlyphCoverageTable ct;
  2681. if (co > 0) {
  2682. ct = readCoverageTable(tableTag + " chained contextual positioning coverage", subtableOffset + co);
  2683. } else {
  2684. ct = null;
  2685. }
  2686. // read rule sets
  2687. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ];
  2688. String header = null;
  2689. for (int i = 0; i < nrs; i++) {
  2690. GlyphTable.RuleSet rs;
  2691. int rso = rsoa [ i ];
  2692. if (rso > 0) {
  2693. // seek to rule set [ i ]
  2694. in.seekSet(subtableOffset + rso);
  2695. // read rule count
  2696. int nr = in.readTTFUShort();
  2697. // read rule offsets
  2698. int[] roa = new int [ nr ];
  2699. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  2700. for (int j = 0; j < nr; j++) {
  2701. roa [ j ] = in.readTTFUShort();
  2702. }
  2703. // read glyph sequence rules
  2704. for (int j = 0; j < nr; j++) {
  2705. GlyphTable.ChainedGlyphSequenceRule r;
  2706. int ro = roa [ j ];
  2707. if (ro > 0) {
  2708. // seek to rule [ j ]
  2709. in.seekSet(subtableOffset + rso + ro);
  2710. // read backtrack glyph count
  2711. int nbg = in.readTTFUShort();
  2712. // read backtrack glyphs
  2713. int[] backtrackGlyphs = new int [ nbg ];
  2714. for (int k = 0, nk = backtrackGlyphs.length; k < nk; k++) {
  2715. backtrackGlyphs [ k ] = in.readTTFUShort();
  2716. }
  2717. // read input glyph count
  2718. int nig = in.readTTFUShort();
  2719. // read glyphs
  2720. int[] glyphs = new int [ nig - 1 ];
  2721. for (int k = 0, nk = glyphs.length; k < nk; k++) {
  2722. glyphs [ k ] = in.readTTFUShort();
  2723. }
  2724. // read lookahead glyph count
  2725. int nlg = in.readTTFUShort();
  2726. // read lookahead glyphs
  2727. int[] lookaheadGlyphs = new int [ nlg ];
  2728. for (int k = 0, nk = lookaheadGlyphs.length; k < nk; k++) {
  2729. lookaheadGlyphs [ k ] = in.readTTFUShort();
  2730. }
  2731. // read rule lookup count
  2732. int nl = in.readTTFUShort();
  2733. // read rule lookups
  2734. if (log.isDebugEnabled()) {
  2735. header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: ";
  2736. }
  2737. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2738. r = new GlyphTable.ChainedGlyphSequenceRule(lookups, nig, glyphs, backtrackGlyphs, lookaheadGlyphs);
  2739. } else {
  2740. r = null;
  2741. }
  2742. ra [ j ] = r;
  2743. }
  2744. rs = new GlyphTable.HomogeneousRuleSet(ra);
  2745. } else {
  2746. rs = null;
  2747. }
  2748. rsa [ i ] = rs;
  2749. }
  2750. // store results
  2751. seMapping = ct;
  2752. seEntries.add(rsa);
  2753. }
  2754. private void readChainedContextualPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2755. String tableTag = "GPOS";
  2756. in.seekSet(subtableOffset);
  2757. // skip over format (already known)
  2758. in.skip(2);
  2759. // read coverage offset
  2760. int co = in.readTTFUShort();
  2761. // read backtrack class def table offset
  2762. int bcdo = in.readTTFUShort();
  2763. // read input class def table offset
  2764. int icdo = in.readTTFUShort();
  2765. // read lookahead class def table offset
  2766. int lcdo = in.readTTFUShort();
  2767. // read class set count
  2768. int ngc = in.readTTFUShort();
  2769. // read class set offsets
  2770. int[] csoa = new int [ ngc ];
  2771. for (int i = 0; i < ngc; i++) {
  2772. csoa [ i ] = in.readTTFUShort();
  2773. }
  2774. // dump info if debugging
  2775. if (log.isDebugEnabled()) {
  2776. log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyph classes)");
  2777. log.debug(tableTag + " chained contextual positioning coverage table offset: " + co);
  2778. log.debug(tableTag + " chained contextual positioning class set count: " + ngc);
  2779. for (int i = 0; i < ngc; i++) {
  2780. log.debug(tableTag + " chained contextual positioning class set offset[" + i + "]: " + csoa[i]);
  2781. }
  2782. }
  2783. // read coverage table
  2784. GlyphCoverageTable ct;
  2785. if (co > 0) {
  2786. ct = readCoverageTable(tableTag + " chained contextual positioning coverage", subtableOffset + co);
  2787. } else {
  2788. ct = null;
  2789. }
  2790. // read backtrack class definition table
  2791. GlyphClassTable bcdt;
  2792. if (bcdo > 0) {
  2793. bcdt = readClassDefTable(tableTag + " contextual positioning backtrack class definition", subtableOffset + bcdo);
  2794. } else {
  2795. bcdt = null;
  2796. }
  2797. // read input class definition table
  2798. GlyphClassTable icdt;
  2799. if (icdo > 0) {
  2800. icdt = readClassDefTable(tableTag + " contextual positioning input class definition", subtableOffset + icdo);
  2801. } else {
  2802. icdt = null;
  2803. }
  2804. // read lookahead class definition table
  2805. GlyphClassTable lcdt;
  2806. if (lcdo > 0) {
  2807. lcdt = readClassDefTable(tableTag + " contextual positioning lookahead class definition", subtableOffset + lcdo);
  2808. } else {
  2809. lcdt = null;
  2810. }
  2811. // read rule sets
  2812. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ];
  2813. String header = null;
  2814. for (int i = 0; i < ngc; i++) {
  2815. int cso = csoa [ i ];
  2816. GlyphTable.RuleSet rs;
  2817. if (cso > 0) {
  2818. // seek to rule set [ i ]
  2819. in.seekSet(subtableOffset + cso);
  2820. // read rule count
  2821. int nr = in.readTTFUShort();
  2822. // read rule offsets
  2823. int[] roa = new int [ nr ];
  2824. GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ];
  2825. for (int j = 0; j < nr; j++) {
  2826. roa [ j ] = in.readTTFUShort();
  2827. }
  2828. // read glyph sequence rules
  2829. for (int j = 0; j < nr; j++) {
  2830. GlyphTable.ChainedClassSequenceRule r;
  2831. int ro = roa [ j ];
  2832. if (ro > 0) {
  2833. // seek to rule [ j ]
  2834. in.seekSet(subtableOffset + cso + ro);
  2835. // read backtrack glyph class count
  2836. int nbc = in.readTTFUShort();
  2837. // read backtrack glyph classes
  2838. int[] backtrackClasses = new int [ nbc ];
  2839. for (int k = 0, nk = backtrackClasses.length; k < nk; k++) {
  2840. backtrackClasses [ k ] = in.readTTFUShort();
  2841. }
  2842. // read input glyph class count
  2843. int nic = in.readTTFUShort();
  2844. // read input glyph classes
  2845. int[] classes = new int [ nic - 1 ];
  2846. for (int k = 0, nk = classes.length; k < nk; k++) {
  2847. classes [ k ] = in.readTTFUShort();
  2848. }
  2849. // read lookahead glyph class count
  2850. int nlc = in.readTTFUShort();
  2851. // read lookahead glyph classes
  2852. int[] lookaheadClasses = new int [ nlc ];
  2853. for (int k = 0, nk = lookaheadClasses.length; k < nk; k++) {
  2854. lookaheadClasses [ k ] = in.readTTFUShort();
  2855. }
  2856. // read rule lookup count
  2857. int nl = in.readTTFUShort();
  2858. // read rule lookups
  2859. if (log.isDebugEnabled()) {
  2860. header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: ";
  2861. }
  2862. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2863. r = new GlyphTable.ChainedClassSequenceRule(lookups, nic, classes, backtrackClasses, lookaheadClasses);
  2864. } else {
  2865. r = null;
  2866. }
  2867. ra [ j ] = r;
  2868. }
  2869. rs = new GlyphTable.HomogeneousRuleSet(ra);
  2870. } else {
  2871. rs = null;
  2872. }
  2873. rsa [ i ] = rs;
  2874. }
  2875. // store results
  2876. seMapping = ct;
  2877. seEntries.add(icdt);
  2878. seEntries.add(bcdt);
  2879. seEntries.add(lcdt);
  2880. seEntries.add(Integer.valueOf(ngc));
  2881. seEntries.add(rsa);
  2882. }
  2883. private void readChainedContextualPosTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException {
  2884. String tableTag = "GPOS";
  2885. in.seekSet(subtableOffset);
  2886. // skip over format (already known)
  2887. in.skip(2);
  2888. // read backtrack glyph count
  2889. int nbg = in.readTTFUShort();
  2890. // read backtrack glyph coverage offsets
  2891. int[] bgcoa = new int [ nbg ];
  2892. for (int i = 0; i < nbg; i++) {
  2893. bgcoa [ i ] = in.readTTFUShort();
  2894. }
  2895. // read input glyph count
  2896. int nig = in.readTTFUShort();
  2897. // read backtrack glyph coverage offsets
  2898. int[] igcoa = new int [ nig ];
  2899. for (int i = 0; i < nig; i++) {
  2900. igcoa [ i ] = in.readTTFUShort();
  2901. }
  2902. // read lookahead glyph count
  2903. int nlg = in.readTTFUShort();
  2904. // read backtrack glyph coverage offsets
  2905. int[] lgcoa = new int [ nlg ];
  2906. for (int i = 0; i < nlg; i++) {
  2907. lgcoa [ i ] = in.readTTFUShort();
  2908. }
  2909. // read positioning lookup count
  2910. int nl = in.readTTFUShort();
  2911. // dump info if debugging
  2912. if (log.isDebugEnabled()) {
  2913. log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyph sets)");
  2914. log.debug(tableTag + " chained contextual positioning backtrack glyph count: " + nbg);
  2915. for (int i = 0; i < nbg; i++) {
  2916. log.debug(tableTag + " chained contextual positioning backtrack coverage table offset[" + i + "]: " + bgcoa[i]);
  2917. }
  2918. log.debug(tableTag + " chained contextual positioning input glyph count: " + nig);
  2919. for (int i = 0; i < nig; i++) {
  2920. log.debug(tableTag + " chained contextual positioning input coverage table offset[" + i + "]: " + igcoa[i]);
  2921. }
  2922. log.debug(tableTag + " chained contextual positioning lookahead glyph count: " + nlg);
  2923. for (int i = 0; i < nlg; i++) {
  2924. log.debug(tableTag + " chained contextual positioning lookahead coverage table offset[" + i + "]: " + lgcoa[i]);
  2925. }
  2926. log.debug(tableTag + " chained contextual positioning lookup count: " + nl);
  2927. }
  2928. // read backtrack coverage tables
  2929. GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg];
  2930. for (int i = 0; i < nbg; i++) {
  2931. int bgco = bgcoa [ i ];
  2932. GlyphCoverageTable bgct;
  2933. if (bgco > 0) {
  2934. bgct = readCoverageTable(tableTag + " chained contextual positioning backtrack coverage[" + i + "]", subtableOffset + bgco);
  2935. } else {
  2936. bgct = null;
  2937. }
  2938. bgca[i] = bgct;
  2939. }
  2940. // read input coverage tables
  2941. GlyphCoverageTable[] igca = new GlyphCoverageTable[nig];
  2942. for (int i = 0; i < nig; i++) {
  2943. int igco = igcoa [ i ];
  2944. GlyphCoverageTable igct;
  2945. if (igco > 0) {
  2946. igct = readCoverageTable(tableTag + " chained contextual positioning input coverage[" + i + "]", subtableOffset + igco);
  2947. } else {
  2948. igct = null;
  2949. }
  2950. igca[i] = igct;
  2951. }
  2952. // read lookahead coverage tables
  2953. GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg];
  2954. for (int i = 0; i < nlg; i++) {
  2955. int lgco = lgcoa [ i ];
  2956. GlyphCoverageTable lgct;
  2957. if (lgco > 0) {
  2958. lgct = readCoverageTable(tableTag + " chained contextual positioning lookahead coverage[" + i + "]", subtableOffset + lgco);
  2959. } else {
  2960. lgct = null;
  2961. }
  2962. lgca[i] = lgct;
  2963. }
  2964. // read rule lookups
  2965. String header = null;
  2966. if (log.isDebugEnabled()) {
  2967. header = tableTag + " chained contextual positioning lookups: ";
  2968. }
  2969. GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header);
  2970. // construct rule, rule set, and rule set array
  2971. GlyphTable.Rule r = new GlyphTable.ChainedCoverageSequenceRule(lookups, nig, igca, bgca, lgca);
  2972. GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r});
  2973. GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs};
  2974. // store results
  2975. assert (igca != null) && (igca.length > 0);
  2976. seMapping = igca[0];
  2977. seEntries.add(rsa);
  2978. }
  2979. private int readChainedContextualPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException {
  2980. in.seekSet(subtableOffset);
  2981. // read positioning subtable format
  2982. int sf = in.readTTFUShort();
  2983. if (sf == 1) {
  2984. readChainedContextualPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf);
  2985. } else if (sf == 2) {
  2986. readChainedContextualPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf);
  2987. } else if (sf == 3) {
  2988. readChainedContextualPosTableFormat3(lookupType, lookupFlags, subtableOffset, sf);
  2989. } else {
  2990. throw new AdvancedTypographicTableFormatException("unsupported chained contextual positioning subtable format: " + sf);
  2991. }
  2992. return sf;
  2993. }
  2994. private void readExtensionPosTableFormat1(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset, int subtableFormat) throws IOException {
  2995. String tableTag = "GPOS";
  2996. in.seekSet(subtableOffset);
  2997. // skip over format (already known)
  2998. in.skip(2);
  2999. // read extension lookup type
  3000. int lt = in.readTTFUShort();
  3001. // read extension offset
  3002. long eo = in.readTTFULong();
  3003. // dump info if debugging
  3004. if (log.isDebugEnabled()) {
  3005. log.debug(tableTag + " extension positioning subtable format: " + subtableFormat);
  3006. log.debug(tableTag + " extension positioning lookup type: " + lt);
  3007. log.debug(tableTag + " extension positioning lookup table offset: " + eo);
  3008. }
  3009. // read referenced subtable from extended offset
  3010. readGPOSSubtable(lt, lookupFlags, lookupSequence, subtableSequence, subtableOffset + eo);
  3011. }
  3012. private int readExtensionPosTable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
  3013. in.seekSet(subtableOffset);
  3014. // read positioning subtable format
  3015. int sf = in.readTTFUShort();
  3016. if (sf == 1) {
  3017. readExtensionPosTableFormat1(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset, sf);
  3018. } else {
  3019. throw new AdvancedTypographicTableFormatException("unsupported extension positioning subtable format: " + sf);
  3020. }
  3021. return sf;
  3022. }
  3023. private void readGPOSSubtable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException {
  3024. initATSubState();
  3025. int subtableFormat = -1;
  3026. switch (lookupType) {
  3027. case GPOSLookupType.SINGLE:
  3028. subtableFormat = readSinglePosTable(lookupType, lookupFlags, subtableOffset);
  3029. break;
  3030. case GPOSLookupType.PAIR:
  3031. subtableFormat = readPairPosTable(lookupType, lookupFlags, subtableOffset);
  3032. break;
  3033. case GPOSLookupType.CURSIVE:
  3034. subtableFormat = readCursivePosTable(lookupType, lookupFlags, subtableOffset);
  3035. break;
  3036. case GPOSLookupType.MARK_TO_BASE:
  3037. subtableFormat = readMarkToBasePosTable(lookupType, lookupFlags, subtableOffset);
  3038. break;
  3039. case GPOSLookupType.MARK_TO_LIGATURE:
  3040. subtableFormat = readMarkToLigaturePosTable(lookupType, lookupFlags, subtableOffset);
  3041. break;
  3042. case GPOSLookupType.MARK_TO_MARK:
  3043. subtableFormat = readMarkToMarkPosTable(lookupType, lookupFlags, subtableOffset);
  3044. break;
  3045. case GPOSLookupType.CONTEXTUAL:
  3046. subtableFormat = readContextualPosTable(lookupType, lookupFlags, subtableOffset);
  3047. break;
  3048. case GPOSLookupType.CHAINED_CONTEXTUAL:
  3049. subtableFormat = readChainedContextualPosTable(lookupType, lookupFlags, subtableOffset);
  3050. break;
  3051. case GPOSLookupType.EXTENSION:
  3052. subtableFormat = readExtensionPosTable(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset);
  3053. break;
  3054. default:
  3055. break;
  3056. }
  3057. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_POSITIONING, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat);
  3058. resetATSubState();
  3059. }
  3060. private void readLookupTable(OFTableName tableTag, int lookupSequence, long lookupTable) throws IOException {
  3061. boolean isGSUB = tableTag.equals(OFTableName.GSUB);
  3062. boolean isGPOS = tableTag.equals(OFTableName.GPOS);
  3063. in.seekSet(lookupTable);
  3064. // read lookup type
  3065. int lt = in.readTTFUShort();
  3066. // read lookup flags
  3067. int lf = in.readTTFUShort();
  3068. // read sub-table count
  3069. int ns = in.readTTFUShort();
  3070. // dump info if debugging
  3071. if (log.isDebugEnabled()) {
  3072. String lts;
  3073. if (isGSUB) {
  3074. lts = GSUBLookupType.toString(lt);
  3075. } else if (isGPOS) {
  3076. lts = GPOSLookupType.toString(lt);
  3077. } else {
  3078. lts = "?";
  3079. }
  3080. log.debug(tableTag + " lookup table type: " + lt + " (" + lts + ")");
  3081. log.debug(tableTag + " lookup table flags: " + lf + " (" + LookupFlag.toString(lf) + ")");
  3082. log.debug(tableTag + " lookup table subtable count: " + ns);
  3083. }
  3084. // read subtable offsets
  3085. int[] soa = new int[ns];
  3086. for (int i = 0; i < ns; i++) {
  3087. int so = in.readTTFUShort();
  3088. if (log.isDebugEnabled()) {
  3089. log.debug(tableTag + " lookup table subtable offset: " + so);
  3090. }
  3091. soa[i] = so;
  3092. }
  3093. // read mark filtering set
  3094. if ((lf & LookupFlag.USE_MARK_FILTERING_SET) != 0) {
  3095. // read mark filtering set
  3096. int fs = in.readTTFUShort();
  3097. // dump info if debugging
  3098. if (log.isDebugEnabled()) {
  3099. log.debug(tableTag + " lookup table mark filter set: " + fs);
  3100. }
  3101. }
  3102. // read subtables
  3103. for (int i = 0; i < ns; i++) {
  3104. int so = soa[i];
  3105. if (isGSUB) {
  3106. readGSUBSubtable(lt, lf, lookupSequence, i, lookupTable + so);
  3107. } else if (isGPOS) {
  3108. readGPOSSubtable(lt, lf, lookupSequence, i, lookupTable + so);
  3109. }
  3110. }
  3111. }
  3112. private void readLookupList(OFTableName tableTag, long lookupList) throws IOException {
  3113. in.seekSet(lookupList);
  3114. // read lookup record count
  3115. int nl = in.readTTFUShort();
  3116. if (log.isDebugEnabled()) {
  3117. log.debug(tableTag + " lookup list record count: " + nl);
  3118. }
  3119. if (nl > 0) {
  3120. int[] loa = new int[nl];
  3121. // read lookup records
  3122. for (int i = 0, n = nl; i < n; i++) {
  3123. int lo = in.readTTFUShort();
  3124. if (log.isDebugEnabled()) {
  3125. log.debug(tableTag + " lookup table offset: " + lo);
  3126. }
  3127. loa[i] = lo;
  3128. }
  3129. // read lookup tables
  3130. for (int i = 0, n = nl; i < n; i++) {
  3131. if (log.isDebugEnabled()) {
  3132. log.debug(tableTag + " lookup index: " + i);
  3133. }
  3134. readLookupTable(tableTag, i, lookupList + loa [ i ]);
  3135. }
  3136. }
  3137. }
  3138. /**
  3139. * Read the common layout tables (used by GSUB and GPOS).
  3140. * @param tableTag tag of table being read
  3141. * @param scriptList offset to script list from beginning of font file
  3142. * @param featureList offset to feature list from beginning of font file
  3143. * @param lookupList offset to lookup list from beginning of font file
  3144. * @throws IOException In case of a I/O problem
  3145. */
  3146. private void readCommonLayoutTables(OFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException {
  3147. if (scriptList > 0) {
  3148. readScriptList(tableTag, scriptList);
  3149. }
  3150. if (featureList > 0) {
  3151. readFeatureList(tableTag, featureList);
  3152. }
  3153. if (lookupList > 0) {
  3154. readLookupList(tableTag, lookupList);
  3155. }
  3156. }
  3157. private void readGDEFClassDefTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
  3158. initATSubState();
  3159. in.seekSet(subtableOffset);
  3160. // subtable is a bare class definition table
  3161. GlyphClassTable ct = readClassDefTable(tableTag + " glyph class definition table", subtableOffset);
  3162. // store results
  3163. seMapping = ct;
  3164. // extract subtable
  3165. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.GLYPH_CLASS, 0, lookupSequence, 0, 1);
  3166. resetATSubState();
  3167. }
  3168. private void readGDEFAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
  3169. initATSubState();
  3170. in.seekSet(subtableOffset);
  3171. // read coverage offset
  3172. int co = in.readTTFUShort();
  3173. // dump info if debugging
  3174. if (log.isDebugEnabled()) {
  3175. log.debug(tableTag + " attachment point coverage table offset: " + co);
  3176. }
  3177. // read coverage table
  3178. GlyphCoverageTable ct = readCoverageTable(tableTag + " attachment point coverage", subtableOffset + co);
  3179. // store results
  3180. seMapping = ct;
  3181. // extract subtable
  3182. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.ATTACHMENT_POINT, 0, lookupSequence, 0, 1);
  3183. resetATSubState();
  3184. }
  3185. private void readGDEFLigatureCaretTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
  3186. initATSubState();
  3187. in.seekSet(subtableOffset);
  3188. // read coverage offset
  3189. int co = in.readTTFUShort();
  3190. // read ligature glyph count
  3191. int nl = in.readTTFUShort();
  3192. // read ligature glyph table offsets
  3193. int[] lgto = new int [ nl ];
  3194. for (int i = 0; i < nl; i++) {
  3195. lgto [ i ] = in.readTTFUShort();
  3196. }
  3197. // dump info if debugging
  3198. if (log.isDebugEnabled()) {
  3199. log.debug(tableTag + " ligature caret coverage table offset: " + co);
  3200. log.debug(tableTag + " ligature caret ligature glyph count: " + nl);
  3201. for (int i = 0; i < nl; i++) {
  3202. log.debug(tableTag + " ligature glyph table offset[" + i + "]: " + lgto[i]);
  3203. }
  3204. }
  3205. // read coverage table
  3206. GlyphCoverageTable ct = readCoverageTable(tableTag + " ligature caret coverage", subtableOffset + co);
  3207. // store results
  3208. seMapping = ct;
  3209. // extract subtable
  3210. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.LIGATURE_CARET, 0, lookupSequence, 0, 1);
  3211. resetATSubState();
  3212. }
  3213. private void readGDEFMarkAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
  3214. initATSubState();
  3215. in.seekSet(subtableOffset);
  3216. // subtable is a bare class definition table
  3217. GlyphClassTable ct = readClassDefTable(tableTag + " glyph class definition table", subtableOffset);
  3218. // store results
  3219. seMapping = ct;
  3220. // extract subtable
  3221. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.MARK_ATTACHMENT, 0, lookupSequence, 0, 1);
  3222. resetATSubState();
  3223. }
  3224. private void readGDEFMarkGlyphsTableFormat1(OFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
  3225. initATSubState();
  3226. in.seekSet(subtableOffset);
  3227. // skip over format (already known)
  3228. in.skip(2);
  3229. // read mark set class count
  3230. int nmc = in.readTTFUShort();
  3231. long[] mso = new long [ nmc ];
  3232. // read mark set coverage offsets
  3233. for (int i = 0; i < nmc; i++) {
  3234. mso [ i ] = in.readTTFULong();
  3235. }
  3236. // dump info if debugging
  3237. if (log.isDebugEnabled()) {
  3238. log.debug(tableTag + " mark set subtable format: " + subtableFormat + " (glyph sets)");
  3239. log.debug(tableTag + " mark set class count: " + nmc);
  3240. for (int i = 0; i < nmc; i++) {
  3241. log.debug(tableTag + " mark set coverage table offset[" + i + "]: " + mso[i]);
  3242. }
  3243. }
  3244. // read mark set coverage tables, one per class
  3245. GlyphCoverageTable[] msca = new GlyphCoverageTable[nmc];
  3246. for (int i = 0; i < nmc; i++) {
  3247. msca[i] = readCoverageTable(tableTag + " mark set coverage[" + i + "]", subtableOffset + mso[i]);
  3248. }
  3249. // create combined class table from per-class coverage tables
  3250. GlyphClassTable ct = GlyphClassTable.createClassTable(Arrays.asList(msca));
  3251. // store results
  3252. seMapping = ct;
  3253. // extract subtable
  3254. extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.MARK_ATTACHMENT, 0, lookupSequence, 0, 1);
  3255. resetATSubState();
  3256. }
  3257. private void readGDEFMarkGlyphsTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
  3258. in.seekSet(subtableOffset);
  3259. // read mark set subtable format
  3260. int sf = in.readTTFUShort();
  3261. if (sf == 1) {
  3262. readGDEFMarkGlyphsTableFormat1(tableTag, lookupSequence, subtableOffset, sf);
  3263. } else {
  3264. throw new AdvancedTypographicTableFormatException("unsupported mark glyph sets subtable format: " + sf);
  3265. }
  3266. }
  3267. /**
  3268. * Read the GDEF table.
  3269. * @throws IOException In case of a I/O problem
  3270. */
  3271. private void readGDEF() throws IOException {
  3272. OFTableName tableTag = OFTableName.GDEF;
  3273. // Initialize temporary state
  3274. initATState();
  3275. // Read glyph definition (GDEF) table
  3276. OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
  3277. if (gdef != null) {
  3278. if (log.isDebugEnabled()) {
  3279. log.debug(tableTag + ": ignoring duplicate table");
  3280. }
  3281. } else if (dirTab != null) {
  3282. otf.seekTab(in, tableTag, 0);
  3283. long version = in.readTTFULong();
  3284. if (log.isDebugEnabled()) {
  3285. log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
  3286. }
  3287. // glyph class definition table offset (may be null)
  3288. int cdo = in.readTTFUShort();
  3289. // attach point list offset (may be null)
  3290. int apo = in.readTTFUShort();
  3291. // ligature caret list offset (may be null)
  3292. int lco = in.readTTFUShort();
  3293. // mark attach class definition table offset (may be null)
  3294. int mao = in.readTTFUShort();
  3295. // mark glyph sets definition table offset (may be null)
  3296. int mgo;
  3297. if (version >= 0x00010002) {
  3298. mgo = in.readTTFUShort();
  3299. } else {
  3300. mgo = 0;
  3301. }
  3302. if (log.isDebugEnabled()) {
  3303. log.debug(tableTag + " glyph class definition table offset: " + cdo);
  3304. log.debug(tableTag + " attachment point list offset: " + apo);
  3305. log.debug(tableTag + " ligature caret list offset: " + lco);
  3306. log.debug(tableTag + " mark attachment class definition table offset: " + mao);
  3307. log.debug(tableTag + " mark glyph set definitions table offset: " + mgo);
  3308. }
  3309. // initialize subtable sequence number
  3310. int seqno = 0;
  3311. // obtain offset to start of gdef table
  3312. long to = dirTab.getOffset();
  3313. // (optionally) read glyph class definition subtable
  3314. if (cdo != 0) {
  3315. readGDEFClassDefTable(tableTag, seqno++, to + cdo);
  3316. }
  3317. // (optionally) read glyph attachment point subtable
  3318. if (apo != 0) {
  3319. readGDEFAttachmentTable(tableTag, seqno++, to + apo);
  3320. }
  3321. // (optionally) read ligature caret subtable
  3322. if (lco != 0) {
  3323. readGDEFLigatureCaretTable(tableTag, seqno++, to + lco);
  3324. }
  3325. // (optionally) read mark attachment class subtable
  3326. if (mao != 0) {
  3327. readGDEFMarkAttachmentTable(tableTag, seqno++, to + mao);
  3328. }
  3329. // (optionally) read mark glyph sets subtable
  3330. if (mgo != 0) {
  3331. readGDEFMarkGlyphsTable(tableTag, seqno++, to + mgo);
  3332. }
  3333. GlyphDefinitionTable gdef;
  3334. if ((gdef = constructGDEF()) != null) {
  3335. this.gdef = gdef;
  3336. }
  3337. }
  3338. }
  3339. /**
  3340. * Read the GSUB table.
  3341. * @throws IOException In case of a I/O problem
  3342. */
  3343. private void readGSUB() throws IOException {
  3344. OFTableName tableTag = OFTableName.GSUB;
  3345. // Initialize temporary state
  3346. initATState();
  3347. // Read glyph substitution (GSUB) table
  3348. OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
  3349. if (gpos != null) {
  3350. if (log.isDebugEnabled()) {
  3351. log.debug(tableTag + ": ignoring duplicate table");
  3352. }
  3353. } else if (dirTab != null) {
  3354. otf.seekTab(in, tableTag, 0);
  3355. int version = in.readTTFLong();
  3356. if (log.isDebugEnabled()) {
  3357. log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
  3358. }
  3359. int slo = in.readTTFUShort();
  3360. int flo = in.readTTFUShort();
  3361. int llo = in.readTTFUShort();
  3362. if (log.isDebugEnabled()) {
  3363. log.debug(tableTag + " script list offset: " + slo);
  3364. log.debug(tableTag + " feature list offset: " + flo);
  3365. log.debug(tableTag + " lookup list offset: " + llo);
  3366. }
  3367. long to = dirTab.getOffset();
  3368. readCommonLayoutTables(tableTag, to + slo, to + flo, to + llo);
  3369. GlyphSubstitutionTable gsub;
  3370. if ((gsub = constructGSUB()) != null) {
  3371. this.gsub = gsub;
  3372. }
  3373. }
  3374. }
  3375. /**
  3376. * Read the GPOS table.
  3377. * @throws IOException In case of a I/O problem
  3378. */
  3379. private void readGPOS() throws IOException {
  3380. OFTableName tableTag = OFTableName.GPOS;
  3381. // Initialize temporary state
  3382. initATState();
  3383. // Read glyph positioning (GPOS) table
  3384. OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
  3385. if (gpos != null) {
  3386. if (log.isDebugEnabled()) {
  3387. log.debug(tableTag + ": ignoring duplicate table");
  3388. }
  3389. } else if (dirTab != null) {
  3390. otf.seekTab(in, tableTag, 0);
  3391. int version = in.readTTFLong();
  3392. if (log.isDebugEnabled()) {
  3393. log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
  3394. }
  3395. int slo = in.readTTFUShort();
  3396. int flo = in.readTTFUShort();
  3397. int llo = in.readTTFUShort();
  3398. if (log.isDebugEnabled()) {
  3399. log.debug(tableTag + " script list offset: " + slo);
  3400. log.debug(tableTag + " feature list offset: " + flo);
  3401. log.debug(tableTag + " lookup list offset: " + llo);
  3402. }
  3403. long to = dirTab.getOffset();
  3404. readCommonLayoutTables(tableTag, to + slo, to + flo, to + llo);
  3405. GlyphPositioningTable gpos;
  3406. if ((gpos = constructGPOS()) != null) {
  3407. this.gpos = gpos;
  3408. }
  3409. }
  3410. }
  3411. /**
  3412. * Construct the (internal representation of the) GDEF table based on previously
  3413. * parsed state.
  3414. * @returns glyph definition table or null if insufficient or invalid state
  3415. */
  3416. private GlyphDefinitionTable constructGDEF() {
  3417. GlyphDefinitionTable gdef = null;
  3418. List subtables;
  3419. if ((subtables = constructGDEFSubtables()) != null) {
  3420. if (subtables.size() > 0) {
  3421. gdef = new GlyphDefinitionTable(subtables);
  3422. }
  3423. }
  3424. resetATState();
  3425. return gdef;
  3426. }
  3427. /**
  3428. * Construct the (internal representation of the) GSUB table based on previously
  3429. * parsed state.
  3430. * @returns glyph substitution table or null if insufficient or invalid state
  3431. */
  3432. private GlyphSubstitutionTable constructGSUB() {
  3433. GlyphSubstitutionTable gsub = null;
  3434. Map lookups;
  3435. if ((lookups = constructLookups()) != null) {
  3436. List subtables;
  3437. if ((subtables = constructGSUBSubtables()) != null) {
  3438. if ((lookups.size() > 0) && (subtables.size() > 0)) {
  3439. gsub = new GlyphSubstitutionTable(gdef, lookups, subtables);
  3440. }
  3441. }
  3442. }
  3443. resetATState();
  3444. return gsub;
  3445. }
  3446. /**
  3447. * Construct the (internal representation of the) GPOS table based on previously
  3448. * parsed state.
  3449. * @returns glyph positioning table or null if insufficient or invalid state
  3450. */
  3451. private GlyphPositioningTable constructGPOS() {
  3452. GlyphPositioningTable gpos = null;
  3453. Map lookups;
  3454. if ((lookups = constructLookups()) != null) {
  3455. List subtables;
  3456. if ((subtables = constructGPOSSubtables()) != null) {
  3457. if ((lookups.size() > 0) && (subtables.size() > 0)) {
  3458. gpos = new GlyphPositioningTable(gdef, lookups, subtables);
  3459. }
  3460. }
  3461. }
  3462. resetATState();
  3463. return gpos;
  3464. }
  3465. private void constructLookupsFeature(Map lookups, String st, String lt, String fid) {
  3466. Object[] fp = (Object[]) seFeatures.get(fid);
  3467. if (fp != null) {
  3468. assert fp.length == 2;
  3469. String ft = (String) fp[0]; // feature tag
  3470. List/*<String>*/ lul = (List) fp[1]; // list of lookup table ids
  3471. if ((ft != null) && (lul != null) && (lul.size() > 0)) {
  3472. GlyphTable.LookupSpec ls = new GlyphTable.LookupSpec(st, lt, ft);
  3473. lookups.put(ls, lul);
  3474. }
  3475. }
  3476. }
  3477. private void constructLookupsFeatures(Map lookups, String st, String lt, List/*<String>*/ fids) {
  3478. for (Iterator fit = fids.iterator(); fit.hasNext();) {
  3479. String fid = (String) fit.next();
  3480. constructLookupsFeature(lookups, st, lt, fid);
  3481. }
  3482. }
  3483. private void constructLookupsLanguage(Map lookups, String st, String lt, Map/*<String,Object[2]>*/ languages) {
  3484. Object[] lp = (Object[]) languages.get(lt);
  3485. if (lp != null) {
  3486. assert lp.length == 2;
  3487. if (lp[0] != null) { // required feature id
  3488. constructLookupsFeature(lookups, st, lt, (String) lp[0]);
  3489. }
  3490. if (lp[1] != null) { // non-required features ids
  3491. constructLookupsFeatures(lookups, st, lt, (List) lp[1]);
  3492. }
  3493. }
  3494. }
  3495. private void constructLookupsLanguages(Map lookups, String st, List/*<String>*/ ll, Map/*<String,Object[2]>*/ languages) {
  3496. for (Iterator lit = ll.iterator(); lit.hasNext();) {
  3497. String lt = (String) lit.next();
  3498. constructLookupsLanguage(lookups, st, lt, languages);
  3499. }
  3500. }
  3501. private Map constructLookups() {
  3502. Map/*<GlyphTable.LookupSpec,List<String>>*/ lookups = new java.util.LinkedHashMap();
  3503. for (Iterator sit = seScripts.keySet().iterator(); sit.hasNext();) {
  3504. String st = (String) sit.next();
  3505. Object[] sp = (Object[]) seScripts.get(st);
  3506. if (sp != null) {
  3507. assert sp.length == 3;
  3508. Map/*<String,Object[2]>*/ languages = (Map) sp[2];
  3509. if (sp[0] != null) { // default language
  3510. constructLookupsLanguage(lookups, st, (String) sp[0], languages);
  3511. }
  3512. if (sp[1] != null) { // non-default languages
  3513. constructLookupsLanguages(lookups, st, (List) sp[1], languages);
  3514. }
  3515. }
  3516. }
  3517. return lookups;
  3518. }
  3519. private List constructGDEFSubtables() {
  3520. List/*<GlyphDefinitionSubtable>*/ subtables = new java.util.ArrayList();
  3521. if (seSubtables != null) {
  3522. for (Iterator it = seSubtables.iterator(); it.hasNext();) {
  3523. Object[] stp = (Object[]) it.next();
  3524. GlyphSubtable st;
  3525. if ((st = constructGDEFSubtable(stp)) != null) {
  3526. subtables.add(st);
  3527. }
  3528. }
  3529. }
  3530. return subtables;
  3531. }
  3532. private GlyphSubtable constructGDEFSubtable(Object[] stp) {
  3533. GlyphSubtable st = null;
  3534. assert (stp != null) && (stp.length == 8);
  3535. Integer tt = (Integer) stp[0]; // table type
  3536. Integer lt = (Integer) stp[1]; // lookup type
  3537. Integer ln = (Integer) stp[2]; // lookup sequence number
  3538. Integer lf = (Integer) stp[3]; // lookup flags
  3539. Integer sn = (Integer) stp[4]; // subtable sequence number
  3540. Integer sf = (Integer) stp[5]; // subtable format
  3541. GlyphMappingTable mapping = (GlyphMappingTable) stp[6];
  3542. List entries = (List) stp[7];
  3543. if (tt.intValue() == GlyphTable.GLYPH_TABLE_TYPE_DEFINITION) {
  3544. int type = GDEFLookupType.getSubtableType(lt.intValue());
  3545. String lid = "lu" + ln.intValue();
  3546. int sequence = sn.intValue();
  3547. int flags = lf.intValue();
  3548. int format = sf.intValue();
  3549. st = GlyphDefinitionTable.createSubtable(type, lid, sequence, flags, format, mapping, entries);
  3550. }
  3551. return st;
  3552. }
  3553. private List constructGSUBSubtables() {
  3554. List/*<GlyphSubtable>*/ subtables = new java.util.ArrayList();
  3555. if (seSubtables != null) {
  3556. for (Iterator it = seSubtables.iterator(); it.hasNext();) {
  3557. Object[] stp = (Object[]) it.next();
  3558. GlyphSubtable st;
  3559. if ((st = constructGSUBSubtable(stp)) != null) {
  3560. subtables.add(st);
  3561. }
  3562. }
  3563. }
  3564. return subtables;
  3565. }
  3566. private GlyphSubtable constructGSUBSubtable(Object[] stp) {
  3567. GlyphSubtable st = null;
  3568. assert (stp != null) && (stp.length == 8);
  3569. Integer tt = (Integer) stp[0]; // table type
  3570. Integer lt = (Integer) stp[1]; // lookup type
  3571. Integer ln = (Integer) stp[2]; // lookup sequence number
  3572. Integer lf = (Integer) stp[3]; // lookup flags
  3573. Integer sn = (Integer) stp[4]; // subtable sequence number
  3574. Integer sf = (Integer) stp[5]; // subtable format
  3575. GlyphCoverageTable coverage = (GlyphCoverageTable) stp[6];
  3576. List entries = (List) stp[7];
  3577. if (tt.intValue() == GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION) {
  3578. int type = GSUBLookupType.getSubtableType(lt.intValue());
  3579. String lid = "lu" + ln.intValue();
  3580. int sequence = sn.intValue();
  3581. int flags = lf.intValue();
  3582. int format = sf.intValue();
  3583. st = GlyphSubstitutionTable.createSubtable(type, lid, sequence, flags, format, coverage, entries);
  3584. }
  3585. return st;
  3586. }
  3587. private List constructGPOSSubtables() {
  3588. List/*<GlyphSubtable>*/ subtables = new java.util.ArrayList();
  3589. if (seSubtables != null) {
  3590. for (Iterator it = seSubtables.iterator(); it.hasNext();) {
  3591. Object[] stp = (Object[]) it.next();
  3592. GlyphSubtable st;
  3593. if ((st = constructGPOSSubtable(stp)) != null) {
  3594. subtables.add(st);
  3595. }
  3596. }
  3597. }
  3598. return subtables;
  3599. }
  3600. private GlyphSubtable constructGPOSSubtable(Object[] stp) {
  3601. GlyphSubtable st = null;
  3602. assert (stp != null) && (stp.length == 8);
  3603. Integer tt = (Integer) stp[0]; // table type
  3604. Integer lt = (Integer) stp[1]; // lookup type
  3605. Integer ln = (Integer) stp[2]; // lookup sequence number
  3606. Integer lf = (Integer) stp[3]; // lookup flags
  3607. Integer sn = (Integer) stp[4]; // subtable sequence number
  3608. Integer sf = (Integer) stp[5]; // subtable format
  3609. GlyphCoverageTable coverage = (GlyphCoverageTable) stp[6];
  3610. List entries = (List) stp[7];
  3611. if (tt.intValue() == GlyphTable.GLYPH_TABLE_TYPE_POSITIONING) {
  3612. int type = GSUBLookupType.getSubtableType(lt.intValue());
  3613. String lid = "lu" + ln.intValue();
  3614. int sequence = sn.intValue();
  3615. int flags = lf.intValue();
  3616. int format = sf.intValue();
  3617. st = GlyphPositioningTable.createSubtable(type, lid, sequence, flags, format, coverage, entries);
  3618. }
  3619. return st;
  3620. }
  3621. private void initATState() {
  3622. seScripts = new java.util.LinkedHashMap();
  3623. seLanguages = new java.util.LinkedHashMap();
  3624. seFeatures = new java.util.LinkedHashMap();
  3625. seSubtables = new java.util.ArrayList();
  3626. resetATSubState();
  3627. }
  3628. private void resetATState() {
  3629. seScripts = null;
  3630. seLanguages = null;
  3631. seFeatures = null;
  3632. seSubtables = null;
  3633. resetATSubState();
  3634. }
  3635. private void initATSubState() {
  3636. seMapping = null;
  3637. seEntries = new java.util.ArrayList();
  3638. }
  3639. private void extractSESubState(int tableType, int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, int subtableFormat) {
  3640. if (seEntries != null) {
  3641. if ((tableType == GlyphTable.GLYPH_TABLE_TYPE_DEFINITION) || (seEntries.size() > 0)) {
  3642. if (seSubtables != null) {
  3643. Integer tt = Integer.valueOf(tableType);
  3644. Integer lt = Integer.valueOf(lookupType);
  3645. Integer ln = Integer.valueOf(lookupSequence);
  3646. Integer lf = Integer.valueOf(lookupFlags);
  3647. Integer sn = Integer.valueOf(subtableSequence);
  3648. Integer sf = Integer.valueOf(subtableFormat);
  3649. seSubtables.add(new Object[] { tt, lt, ln, lf, sn, sf, seMapping, seEntries });
  3650. }
  3651. }
  3652. }
  3653. }
  3654. private void resetATSubState() {
  3655. seMapping = null;
  3656. seEntries = null;
  3657. }
  3658. private void resetATStateAll() {
  3659. resetATState();
  3660. gdef = null;
  3661. gsub = null;
  3662. gpos = null;
  3663. }
  3664. /** helper method for formatting an integer array for output */
  3665. private String toString(int[] ia) {
  3666. StringBuffer sb = new StringBuffer();
  3667. if ((ia == null) || (ia.length == 0)) {
  3668. sb.append('-');
  3669. } else {
  3670. boolean first = true;
  3671. for (int i = 0; i < ia.length; i++) {
  3672. if (!first) {
  3673. sb.append(' ');
  3674. } else {
  3675. first = false;
  3676. }
  3677. sb.append(ia[i]);
  3678. }
  3679. }
  3680. return sb.toString();
  3681. }
  3682. }