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.

DevanagariScriptProcessor.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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.scripts;
  19. import org.apache.commons.logging.Log;
  20. import org.apache.commons.logging.LogFactory;
  21. import org.apache.fop.complexscripts.util.CharAssociation;
  22. import org.apache.fop.complexscripts.util.GlyphSequence;
  23. // CSOFF: LineLengthCheck
  24. /**
  25. * <p>The <code>DevanagariScriptProcessor</code> class implements a script processor for
  26. * performing glyph substitution and positioning operations on content associated with the Devanagari script.</p>
  27. *
  28. * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
  29. */
  30. public class DevanagariScriptProcessor extends IndicScriptProcessor {
  31. /** logging instance */
  32. private static final Log log = LogFactory.getLog(DevanagariScriptProcessor.class);
  33. DevanagariScriptProcessor(String script) {
  34. super(script);
  35. }
  36. @Override
  37. protected Class<? extends DevanagariSyllabizer> getSyllabizerClass() {
  38. return DevanagariSyllabizer.class;
  39. }
  40. @Override
  41. // find rightmost pre-base matra
  42. protected int findPreBaseMatra(GlyphSequence gs) {
  43. int ng = gs.getGlyphCount();
  44. int lk = -1;
  45. for (int i = ng; i > 0; i--) {
  46. int k = i - 1;
  47. if (containsPreBaseMatra(gs, k)) {
  48. lk = k;
  49. break;
  50. }
  51. }
  52. return lk;
  53. }
  54. @Override
  55. // find leftmost pre-base matra target, starting from source
  56. protected int findPreBaseMatraTarget(GlyphSequence gs, int source) {
  57. int ng = gs.getGlyphCount();
  58. int lk = -1;
  59. for (int i = (source < ng) ? source : ng; i > 0; i--) {
  60. int k = i - 1;
  61. if (containsConsonant(gs, k)) {
  62. if (containsHalfConsonant(gs, k)) {
  63. lk = k;
  64. } else if (lk == -1) {
  65. lk = k;
  66. } else {
  67. break;
  68. }
  69. }
  70. }
  71. return lk;
  72. }
  73. private static boolean containsPreBaseMatra(GlyphSequence gs, int k) {
  74. CharAssociation a = gs.getAssociation(k);
  75. int[] ca = gs.getCharacterArray(false);
  76. for (int i = a.getStart(), e = a.getEnd(); i < e; i++) {
  77. if (isPreM(ca [ i ])) {
  78. return true;
  79. }
  80. }
  81. return false;
  82. }
  83. private static boolean containsConsonant(GlyphSequence gs, int k) {
  84. CharAssociation a = gs.getAssociation(k);
  85. int[] ca = gs.getCharacterArray(false);
  86. for (int i = a.getStart(), e = a.getEnd(); i < e; i++) {
  87. if (isC(ca [ i ])) {
  88. return true;
  89. }
  90. }
  91. return false;
  92. }
  93. private static boolean containsHalfConsonant(GlyphSequence gs, int k) {
  94. Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half");
  95. return (half != null) ? half : false;
  96. }
  97. @Override
  98. protected int findReph(GlyphSequence gs) {
  99. int ng = gs.getGlyphCount();
  100. int li = -1;
  101. for (int i = 0; i < ng; i++) {
  102. if (containsReph(gs, i)) {
  103. li = i;
  104. break;
  105. }
  106. }
  107. return li;
  108. }
  109. @Override
  110. protected int findRephTarget(GlyphSequence gs, int source) {
  111. int ng = gs.getGlyphCount();
  112. int c1 = -1;
  113. int c2 = -1;
  114. // first candidate target is after first non-half consonant
  115. for (int i = 0; i < ng; i++) {
  116. if ((i != source) && containsConsonant(gs, i)) {
  117. if (!containsHalfConsonant(gs, i)) {
  118. c1 = i + 1;
  119. break;
  120. }
  121. }
  122. }
  123. // second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark
  124. for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) {
  125. if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) {
  126. c2 = i + 1;
  127. } else if (containsOtherMark(gs, i)) {
  128. c2 = i;
  129. break;
  130. }
  131. }
  132. if (c2 >= 0) {
  133. return c2;
  134. } else if (c1 >= 0) {
  135. return c1;
  136. } else {
  137. return source;
  138. }
  139. }
  140. private static boolean containsReph(GlyphSequence gs, int k) {
  141. Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf");
  142. return (rphf != null) ? rphf : false;
  143. }
  144. private static boolean containsMatra(GlyphSequence gs, int k) {
  145. CharAssociation a = gs.getAssociation(k);
  146. int[] ca = gs.getCharacterArray(false);
  147. for (int i = a.getStart(), e = a.getEnd(); i < e; i++) {
  148. if (isM(ca [ i ])) {
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. private static boolean containsOtherMark(GlyphSequence gs, int k) {
  155. CharAssociation a = gs.getAssociation(k);
  156. int[] ca = gs.getCharacterArray(false);
  157. for (int i = a.getStart(), e = a.getEnd(); i < e; i++) {
  158. switch (typeOf(ca [ i ])) {
  159. case C_T: // tone (e.g., udatta, anudatta)
  160. case C_A: // accent (e.g., acute, grave)
  161. case C_O: // other (e.g., candrabindu, anusvara, visarga, etc)
  162. return true;
  163. default:
  164. break;
  165. }
  166. }
  167. return false;
  168. }
  169. private static class DevanagariSyllabizer extends DefaultSyllabizer {
  170. DevanagariSyllabizer(String script, String language) {
  171. super(script, language);
  172. }
  173. @Override
  174. // | C ...
  175. protected int findStartOfSyllable(int[] ca, int s, int e) {
  176. if ((s < 0) || (s >= e)) {
  177. return -1;
  178. } else {
  179. while (s < e) {
  180. int c = ca [ s ];
  181. if (isC(c)) {
  182. break;
  183. } else {
  184. s++;
  185. }
  186. }
  187. return s;
  188. }
  189. }
  190. @Override
  191. // D* L? | ...
  192. protected int findEndOfSyllable(int[] ca, int s, int e) {
  193. if ((s < 0) || (s >= e)) {
  194. return -1;
  195. } else {
  196. int nd = 0;
  197. int nl = 0;
  198. int i;
  199. // consume dead consonants
  200. while ((i = isDeadConsonant(ca, s, e)) > s) {
  201. s = i;
  202. nd++;
  203. }
  204. // consume zero or one live consonant
  205. if ((i = isLiveConsonant(ca, s, e)) > s) {
  206. s = i;
  207. nl++;
  208. }
  209. return ((nd > 0) || (nl > 0)) ? s : -1;
  210. }
  211. }
  212. // D := ( C N? H )?
  213. private int isDeadConsonant(int[] ca, int s, int e) {
  214. if (s < 0) {
  215. return -1;
  216. } else {
  217. int c;
  218. int i = 0;
  219. int nc = 0;
  220. int nh = 0;
  221. do {
  222. // C
  223. if ((s + i) < e) {
  224. c = ca [ s + i ];
  225. if (isC(c)) {
  226. i++;
  227. nc++;
  228. } else {
  229. break;
  230. }
  231. }
  232. // N?
  233. if ((s + i) < e) {
  234. c = ca [ s + 1 ];
  235. if (isN(c)) {
  236. i++;
  237. }
  238. }
  239. // H
  240. if ((s + i) < e) {
  241. c = ca [ s + i ];
  242. if (isH(c)) {
  243. i++;
  244. nh++;
  245. } else {
  246. break;
  247. }
  248. }
  249. } while (false);
  250. return (nc > 0) && (nh > 0) ? s + i : -1;
  251. }
  252. }
  253. // L := ( (C|V) N? X* )?; where X = ( MATRA | ACCENT MARK | TONE MARK | OTHER MARK )
  254. private int isLiveConsonant(int[] ca, int s, int e) {
  255. if (s < 0) {
  256. return -1;
  257. } else {
  258. int c;
  259. int i = 0;
  260. int nc = 0;
  261. int nv = 0;
  262. int nx = 0;
  263. do {
  264. // C
  265. if ((s + i) < e) {
  266. c = ca [ s + i ];
  267. if (isC(c)) {
  268. i++;
  269. nc++;
  270. } else if (isV(c)) {
  271. i++;
  272. nv++;
  273. } else {
  274. break;
  275. }
  276. }
  277. // N?
  278. if ((s + i) < e) {
  279. c = ca [ s + i ];
  280. if (isN(c)) {
  281. i++;
  282. }
  283. }
  284. // X*
  285. while ((s + i) < e) {
  286. c = ca [ s + i ];
  287. if (isX(c)) {
  288. i++;
  289. nx++;
  290. } else {
  291. break;
  292. }
  293. }
  294. } while (false);
  295. // if no X but has H, then ignore C|I
  296. if (nx == 0) {
  297. if ((s + i) < e) {
  298. c = ca [ s + i ];
  299. if (isH(c)) {
  300. if (nc > 0) {
  301. nc--;
  302. } else if (nv > 0) {
  303. nv--;
  304. }
  305. }
  306. }
  307. }
  308. return ((nc > 0) || (nv > 0)) ? s + i : -1;
  309. }
  310. }
  311. }
  312. // devanagari character types
  313. static final short C_U = 0; // unassigned
  314. static final short C_C = 1; // consonant
  315. static final short C_V = 2; // vowel
  316. static final short C_M = 3; // vowel sign (matra)
  317. static final short C_S = 4; // symbol or sign
  318. static final short C_T = 5; // tone mark
  319. static final short C_A = 6; // accent mark
  320. static final short C_P = 7; // punctuation
  321. static final short C_D = 8; // digit
  322. static final short C_H = 9; // halant (virama)
  323. static final short C_O = 10; // other signs
  324. static final short C_N = 0x0100; // nukta(ized)
  325. static final short C_R = 0x0200; // reph(ized)
  326. static final short C_PRE = 0x0400; // pre-base
  327. static final short C_M_TYPE = 0x00FF; // type mask
  328. static final short C_M_FLAGS = 0x7F00; // flag mask
  329. // devanagari block range
  330. static final int CCA_START = 0x0900; // first code point mapped by cca
  331. static final int CCA_END = 0x0980; // last code point + 1 mapped by cca
  332. // devanagari character type lookups
  333. static final short[] CCA = {
  334. C_O, // 0x0900 // INVERTED CANDRABINDU
  335. C_O, // 0x0901 // CANDRABINDU
  336. C_O, // 0x0902 // ANUSVARA
  337. C_O, // 0x0903 // VISARGA
  338. C_V, // 0x0904 // SHORT A
  339. C_V, // 0x0905 // A
  340. C_V, // 0x0906 // AA
  341. C_V, // 0x0907 // I
  342. C_V, // 0x0908 // II
  343. C_V, // 0x0909 // U
  344. C_V, // 0x090A // UU
  345. C_V, // 0x090B // VOCALIC R
  346. C_V, // 0x090C // VOCALIC L
  347. C_V, // 0x090D // CANDRA E
  348. C_V, // 0x090E // SHORT E
  349. C_V, // 0x090F // E
  350. C_V, // 0x0910 // AI
  351. C_V, // 0x0911 // CANDRA O
  352. C_V, // 0x0912 // SHORT O
  353. C_V, // 0x0913 // O
  354. C_V, // 0x0914 // AU
  355. C_C, // 0x0915 // KA
  356. C_C, // 0x0916 // KHA
  357. C_C, // 0x0917 // GA
  358. C_C, // 0x0918 // GHA
  359. C_C, // 0x0919 // NGA
  360. C_C, // 0x091A // CA
  361. C_C, // 0x091B // CHA
  362. C_C, // 0x091C // JA
  363. C_C, // 0x091D // JHA
  364. C_C, // 0x091E // NYA
  365. C_C, // 0x091F // TTA
  366. C_C, // 0x0920 // TTHA
  367. C_C, // 0x0921 // DDA
  368. C_C, // 0x0922 // DDHA
  369. C_C, // 0x0923 // NNA
  370. C_C, // 0x0924 // TA
  371. C_C, // 0x0925 // THA
  372. C_C, // 0x0926 // DA
  373. C_C, // 0x0927 // DHA
  374. C_C, // 0x0928 // NA
  375. C_C, // 0x0929 // NNNA
  376. C_C, // 0x092A // PA
  377. C_C, // 0x092B // PHA
  378. C_C, // 0x092C // BA
  379. C_C, // 0x092D // BHA
  380. C_C, // 0x092E // MA
  381. C_C, // 0x092F // YA
  382. C_C | C_R, // 0x0930 // RA
  383. C_C | C_R | C_N, // 0x0931 // RRA = 0930+093C
  384. C_C, // 0x0932 // LA
  385. C_C, // 0x0933 // LLA
  386. C_C, // 0x0934 // LLLA
  387. C_C, // 0x0935 // VA
  388. C_C, // 0x0936 // SHA
  389. C_C, // 0x0937 // SSA
  390. C_C, // 0x0938 // SA
  391. C_C, // 0x0939 // HA
  392. C_M, // 0x093A // OE (KASHMIRI)
  393. C_M, // 0x093B // OOE (KASHMIRI)
  394. C_N, // 0x093C // NUKTA
  395. C_S, // 0x093D // AVAGRAHA
  396. C_M, // 0x093E // AA
  397. C_M | C_PRE, // 0x093F // I
  398. C_M, // 0x0940 // II
  399. C_M, // 0x0941 // U
  400. C_M, // 0x0942 // UU
  401. C_M, // 0x0943 // VOCALIC R
  402. C_M, // 0x0944 // VOCALIC RR
  403. C_M, // 0x0945 // CANDRA E
  404. C_M, // 0x0946 // SHORT E
  405. C_M, // 0x0947 // E
  406. C_M, // 0x0948 // AI
  407. C_M, // 0x0949 // CANDRA O
  408. C_M, // 0x094A // SHORT O
  409. C_M, // 0x094B // O
  410. C_M, // 0x094C // AU
  411. C_H, // 0x094D // VIRAMA (HALANT)
  412. C_M, // 0x094E // PRISHTHAMATRA E
  413. C_M, // 0x094F // AW
  414. C_S, // 0x0950 // OM
  415. C_T, // 0x0951 // UDATTA
  416. C_T, // 0x0952 // ANUDATTA
  417. C_A, // 0x0953 // GRAVE
  418. C_A, // 0x0954 // ACUTE
  419. C_M, // 0x0955 // CANDRA LONG E
  420. C_M, // 0x0956 // UE
  421. C_M, // 0x0957 // UUE
  422. C_C | C_N, // 0x0958 // QA
  423. C_C | C_N, // 0x0959 // KHHA
  424. C_C | C_N, // 0x095A // GHHA
  425. C_C | C_N, // 0x095B // ZA
  426. C_C | C_N, // 0x095C // DDDHA
  427. C_C | C_N, // 0x095D // RHA
  428. C_C | C_N, // 0x095E // FA
  429. C_C | C_N, // 0x095F // YYA
  430. C_V, // 0x0960 // VOCALIC RR
  431. C_V, // 0x0961 // VOCALIC LL
  432. C_M, // 0x0962 // VOCALIC RR
  433. C_M, // 0x0963 // VOCALIC LL
  434. C_P, // 0x0964 // DANDA
  435. C_P, // 0x0965 // DOUBLE DANDA
  436. C_D, // 0x0966 // ZERO
  437. C_D, // 0x0967 // ONE
  438. C_D, // 0x0968 // TWO
  439. C_D, // 0x0969 // THREE
  440. C_D, // 0x096A // FOUR
  441. C_D, // 0x096B // FIVE
  442. C_D, // 0x096C // SIX
  443. C_D, // 0x096D // SEVEN
  444. C_D, // 0x096E // EIGHT
  445. C_D, // 0x096F // NINE
  446. C_S, // 0x0970 // ABBREVIATION SIGN
  447. C_S, // 0x0971 // HIGH SPACING DOT
  448. C_V, // 0x0972 // CANDRA A (MARATHI)
  449. C_V, // 0x0973 // OE (KASHMIRI)
  450. C_V, // 0x0974 // OOE (KASHMIRI)
  451. C_V, // 0x0975 // AW (KASHMIRI)
  452. C_V, // 0x0976 // UE (KASHMIRI)
  453. C_V, // 0x0977 // UUE (KASHMIRI)
  454. C_U, // 0x0978 // UNASSIGNED
  455. C_C, // 0x0979 // ZHA
  456. C_C, // 0x097A // HEAVY YA
  457. C_C, // 0x097B // GGAA (SINDHI)
  458. C_C, // 0x097C // JJA (SINDHI)
  459. C_C, // 0x097D // GLOTTAL STOP (LIMBU)
  460. C_C, // 0x097E // DDDA (SINDHI)
  461. C_C // 0x097F // BBA (SINDHI)
  462. };
  463. static int typeOf(int c) {
  464. if ((c >= CCA_START) && (c < CCA_END)) {
  465. return CCA [ c - CCA_START ] & C_M_TYPE;
  466. } else {
  467. return C_U;
  468. }
  469. }
  470. static boolean isType(int c, int t) {
  471. return typeOf(c) == t;
  472. }
  473. static boolean hasFlag(int c, int f) {
  474. if ((c >= CCA_START) && (c < CCA_END)) {
  475. return (CCA [ c - CCA_START ] & f) == f;
  476. } else {
  477. return false;
  478. }
  479. }
  480. static boolean isC(int c) {
  481. return isType(c, C_C);
  482. }
  483. static boolean isR(int c) {
  484. return isType(c, C_C) && hasR(c);
  485. }
  486. static boolean isV(int c) {
  487. return isType(c, C_V);
  488. }
  489. static boolean isN(int c) {
  490. return c == 0x093C;
  491. }
  492. static boolean isH(int c) {
  493. return c == 0x094D;
  494. }
  495. static boolean isM(int c) {
  496. return isType(c, C_M);
  497. }
  498. static boolean isPreM(int c) {
  499. return isType(c, C_M) && hasFlag(c, C_PRE);
  500. }
  501. static boolean isX(int c) {
  502. switch (typeOf(c)) {
  503. case C_M: // matra (combining vowel)
  504. case C_A: // accent mark
  505. case C_T: // tone mark
  506. case C_O: // other (modifying) mark
  507. return true;
  508. default:
  509. return false;
  510. }
  511. }
  512. static boolean hasR(int c) {
  513. return hasFlag(c, C_R);
  514. }
  515. static boolean hasN(int c) {
  516. return hasFlag(c, C_N);
  517. }
  518. }