PluginProbe
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin / 1.7.3
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin v1.7.3
trunk 1.5 1.6 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 All 71 releases
← All changes | mpdf/classes/indic.php +319 -1600 1.8.31.7.3 View file →
@@ -1,1714 +1,433 @@
1 1 <?php
2 2
3 +class indic {
3 4
4 -class INDIC {
5 +function indic() {
5 6
6 -/* FROM hb-ot-shape-complex-indic-private.hh */
7 -// indic_category
8 -const OT_X = 0;
9 -const OT_C = 1;
10 -const OT_V = 2;
11 -const OT_N = 3;
12 -const OT_H = 4;
13 -const OT_ZWNJ = 5;
14 -const OT_ZWJ = 6;
15 -const OT_M = 7; /* Matra or Dependent Vowel */
16 -const OT_SM = 8;
17 -const OT_VD = 9;
18 -const OT_A = 10;
19 -const OT_NBSP = 11;
20 -const OT_DOTTEDCIRCLE = 12; /* Not in the spec, but special in Uniscribe. /Very very/ special! */
21 -const OT_RS = 13; /* Register Shifter, used in Khmer OT spec */
22 -const OT_Coeng = 14;
23 -const OT_Repha = 15;
24 -const OT_Ra = 16; /* Not explicitly listed in the OT spec, but used in the grammar. */
25 -const OT_CM = 17;
7 +}
26 8
27 9
28 -// Based on indic_category used to make string to find syllables
29 -// OT_ to string character (using e.g. OT_C from INDIC) hb-ot-shape-complex-indic-private.hh
30 -public static $indic_category_char = array(
31 -'x',
32 -'C',
33 -'V',
34 -'N',
35 -'H',
36 -'Z',
37 -'J',
38 -'M',
39 -'S',
40 -'v',
41 -'A', /* Spec gives Andutta U+0952 as OT_A. However, testing shows that Uniscribe
42 - * treats U+0951..U+0952 all as OT_VD - see set_indic_properties */
43 -'s',
44 -'D',
45 -'F', /* Register shift Khmer only */
46 -'G', /* Khmer only */
47 -'r', /* 0D4E (dot reph) only one in Malayalam */
48 -'R',
49 -'m', /* Consonant medial only used in Indic 0A75 in Gurmukhi (0A00..0A7F) : also in Lao, Myanmar, Tai Tham, Javanese & Cham */
50 -);
10 +function substituteIndic($earr, $lang, $font) {
11 + global $voltdata;
51 12
52 -
53 -/* Visual positions in a syllable from left to right. */
54 -/* FROM hb-ot-shape-complex-indic-private.hh */
55 -// indic_position
56 -const POS_START = 0;
57 -
58 -const POS_RA_TO_BECOME_REPH = 1;
59 -const POS_PRE_M = 2;
60 -const POS_PRE_C = 3;
61 -
62 -const POS_BASE_C = 4;
63 -const POS_AFTER_MAIN = 5;
64 -
65 -const POS_ABOVE_C = 6;
66 -
67 -const POS_BEFORE_SUB = 7;
68 -const POS_BELOW_C = 8;
69 -const POS_AFTER_SUB = 9;
70 -
71 -const POS_BEFORE_POST = 10;
72 -const POS_POST_C = 11;
73 -const POS_AFTER_POST = 12;
74 -
75 -const POS_FINAL_C = 13;
76 -const POS_SMVD = 14;
77 -
78 -const POS_END = 15;
79 -
80 -/*
81 -* Basic features.
82 -* These features are applied in order, one at a time, after initial_reordering.
83 -*/
84 -/*
85 - * Must be in the same order as the indic_features array. Ones starting with _ are F_GLOBAL
86 - * Ones without the _ are only applied where the mask says!
87 - */
88 -const _NUKT = 0;
89 -const _AKHN = 1;
90 -const RPHF = 2;
91 -const _RKRF = 3;
92 -const PREF = 4;
93 -const BLWF = 5;
94 -const HALF = 6;
95 -const ABVF = 7;
96 -const PSTF = 8;
97 -const CFAR = 9; // Khmer only
98 -const _VATU = 10;
99 -const _CJCT = 11;
100 -const INIT = 12;
101 -
102 -
103 -public static function set_indic_properties(&$info, $scriptblock ) {
104 - $u = $info['uni'];
105 - $type = self::indic_get_categories($u);
106 - $cat = ($type & 0x7F);
107 - $pos = ($type >> 8);
108 -
109 - /*
110 - * Re-assign category
111 - */
112 -
113 - if ($u == 0x17D1) $cat = self::OT_X;
114 -
115 - if ($cat == self::OT_X && self::in_range($u, 0x17CB, 0x17D3)) { /* Khmer Various signs */
116 - /* These are like Top Matras. */
117 - $cat = self::OT_M;
118 - $pos = self::POS_ABOVE_C;
13 + if (!isset($voltdata[$font])) {
14 + include_once(_MPDF_PATH.'includes/'.$font.'.volt.php');
15 + $voltdata[$font] = $volt;
119 16 }
120 17
121 - if ($u == 0x17C6) $cat = self::OT_N; /* Khmer Bindu doesn't like to be repositioned. */
122 -
123 - if ($u == 0x17D2) $cat = self::OT_Coeng; /* Khmer coeng */
124 -
125 - /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe
126 - * treats U+0951..U+0952 all as OT_VD.
127 - * TESTS:
128 - * U+092E,U+0947,U+0952
129 - * U+092E,U+0952,U+0947
130 - * U+092E,U+0947,U+0951
131 - * U+092E,U+0951,U+0947
132 - * */
133 - //if ($u == 0x0952) $cat = self::OT_A;
134 - if (self::in_range($u, 0x0951, 0x0954))
135 - $cat = self::OT_VD;
136 -
137 - if ($u == 0x200C) $cat = self::OT_ZWNJ;
138 - else if ($u == 0x200D) $cat = self::OT_ZWJ;
139 - else if ($u == 0x25CC) $cat = self::OT_DOTTEDCIRCLE;
140 - else if ($u == 0x0A71) $cat = self::OT_SM; /* GURMUKHI ADDAK. More like consonant medial. like 0A75. */
141 -
142 - if ($cat == self::OT_Repha) {
143 - /* There are two kinds of characters marked as Repha:
144 - * - The ones that are GenCat=Mn are already positioned visually, ie. after base. (eg. Khmer)
145 - * - The ones that are GenCat=Lo is encoded logically, ie. beginning of syllable. (eg. Malayalam)
146 - *
147 - * We recategorize the first kind to look like a Nukta and attached to the base directly.
148 - */
149 - if ($info['general_category'] == UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
150 - $cat = self::OT_N;
18 + foreach($earr as $eid=>$char) {
19 + $earr[$eid] = sprintf("%04s", strtoupper(dechex($char)));
151 20 }
152 -
153 - /*
154 - * Re-assign position.
155 - */
156 -
157 - if ((self::FLAG($cat) & (self::FLAG(self::OT_C) | self::FLAG(self::OT_CM) | self::FLAG(self::OT_Ra) | self::FLAG(self::OT_V) | self::FLAG(self::OT_NBSP) | self::FLAG(self::OT_DOTTEDCIRCLE)))) { // = CONSONANT_FLAGS like is_consonant
158 - if ($scriptblock == UCDN::SCRIPT_KHMER) $pos = self::POS_BELOW_C; /* Khmer differs from Indic here. */
159 - else $pos = self::POS_BASE_C; /* Will recategorize later based on font lookups. */
160 -
161 - if (self::is_ra ($u))
162 - $cat = self::OT_Ra;
21 + $vstr = "0020 ".implode(" ",$earr)." 0020";
22 + //============================
23 + // Common Indic Punctuation marks
24 + // If NOT devanagari
25 + if ($lang!='hi') {
26 + $vstr = str_replace('0964','007C', $vstr); // U+0964 replace with "|"
27 + $vstr = str_replace('0965','007C 007C', $vstr); // U+0964 replace with "|"
163 28 }
164 - else if ($cat == self::OT_M) {
165 - $pos = self::matra_position($u, $pos);
29 + //============================
30 + // Tamil numeral for Zero missing Added mPDF 4.2
31 + if ($lang=='ta') {
32 + $vstr = str_replace('0BE6','0030', $vstr); // U+0BEB replace with "0"
166 33 }
167 - else if ($cat == self::OT_SM || $cat == self::OT_VD) {
168 - $pos = self::POS_SMVD;
169 - }
170 34
171 - if ($u == 0x0B01) $pos = self::POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
35 + //============================
36 + // Re-order vowels
172 37
173 - $info['indic_category'] = $cat;
174 - $info['indic_position'] = $pos;
175 -}
176 -
177 -// syllable_type
178 -const CONSONANT_SYLLABLE = 0;
179 -const VOWEL_SYLLABLE = 1;
180 -const STANDALONE_CLUSTER = 2;
181 -const BROKEN_CLUSTER = 3;
182 -const NON_INDIC_CLUSTER = 4;
183 -
184 -public static function set_syllables(&$o, $s, &$broken_syllables) {
185 - $ptr = 0;
186 - $syllable_serial = 1;
187 - $broken_syllables = false;
188 -
189 - while($ptr < strlen($s)) {
190 - $match = '';
191 - $syllable_length = 1;
192 - $syllable_type = self::NON_INDIC_CLUSTER ;
193 - // CONSONANT_SYLLABLE Consonant syllable
194 - // From OT spec:
195 - if (preg_match('/^([CR]m*[N]?(H[ZJ]?|[ZJ]H))*[CR]m*[N]?[A]?(H[ZJ]?|[M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
196 - // From HarfBuzz:
197 - //if (preg_match('/^r?([CR]J?(Z?[N]{0,2})?[ZJ]?H(J[N]?)?){0,4}[CR]J?(Z?[N]{0,2})?A?((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
198 - $syllable_length = strlen($ma[0]);
199 - $syllable_type = self::CONSONANT_SYLLABLE ;
200 - }
201 - // VOWEL_SYLLABLE Vowel-based syllable
202 - // From OT spec:
203 - else if (preg_match('/^(RH|r)?V[N]?([ZJ]?H[CR]m*|J[CR]m*)?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
204 - // From HarfBuzz:
205 - //else if (preg_match('/^(RH|r)?V(Z?[N]{0,2})?(J|([ZJ]?H(J[N]?)?[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2})/', substr($s,$ptr), $ma)) {
206 - $syllable_length = strlen($ma[0]);
207 - $syllable_type = self::VOWEL_SYLLABLE ;
208 - }
209 -
210 - /* Apply only if it's a word start. */
211 - // STANDALONE_CLUSTER Stand Alone syllable at start of word
212 - // From OT spec:
213 - else if (($ptr==0 ||
214 - $o[$ptr - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER ||
215 - $o[$ptr - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK
216 - )
217 -
218 - && (preg_match('/^(RH|r)?[sD][N]?([ZJ]?H[CR]m*)?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma))) {
219 - // From HarfBuzz:
220 - // && (preg_match('/^(RH|r)?[sD](Z?[N]{0,2})?(([ZJ]?H(J[N]?)?)[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})?(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
221 - $syllable_length = strlen($ma[0]);
222 - $syllable_type = self::STANDALONE_CLUSTER ;
223 - }
224 -
225 - // BROKEN_CLUSTER syllable
226 - else if (preg_match('/^(RH|r)?[N]?([ZJ]?H[CR])?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s,$ptr), $ma)) {
227 - // From HarfBuzz:
228 - //else if (preg_match('/^(RH|r)?(Z?[N]{0,2})?(([ZJ]?H(J[N]?)?)[CR]J?(Z?[N]{0,2})?){0,4}((([ZJ]?H(J[N]?)?)|HZ)|(HJ)?([ZJ]{0,3}M[N]?(H|JHJR)?){0,4})(S[Z]?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
229 - if (strlen($ma[0])) { // May match blank
230 - $syllable_length = strlen($ma[0]);
231 - $syllable_type = self::BROKEN_CLUSTER ;
232 - $broken_syllables = true;
233 - }
234 - }
235 -
236 - for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
237 - $ptr += $syllable_length ;
238 - $syllable_serial++;
239 - if ($syllable_serial == 16) $syllable_serial = 1;
38 + // DEVANAGARI vowel sign matraI[093F] before consonant
39 + if ($lang=='hi') {
40 + $prebasedvowels = "(093F)";
41 + $nukta = "093C";
42 + $halant = "094D";
43 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
44 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
45 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT == VIRAMA
240 46 }
241 -}
242 47
48 + // BENGALI vowels [09BF 09C7 09C8]
49 + else if ($lang=='bn') {
243 50
244 -public static function set_syllables_sinhala(&$o, $s, &$broken_syllables) {
245 - $ptr = 0;
246 - $syllable_serial = 1;
247 - $broken_syllables = false;
51 + // Khanda Ta 09CE not in font -> replace with 09A4|09CD
52 + $vstr = preg_replace('/09CE/','09A4 09CD 200D', $vstr); // mPDF 5.3.09
248 53
249 - while($ptr < strlen($s)) {
250 - $match = '';
251 - $syllable_length = 1;
252 - $syllable_type = self::NON_INDIC_CLUSTER ;
253 - // CONSONANT_SYLLABLE Consonant syllable
254 - // From OT spec:
255 - if (preg_match('/^([CR]HJ|[CR]JH){0,8}[CR][HM]{0,3}[S]{0,1}/', substr($s,$ptr), $ma)) {
256 - $syllable_length = strlen($ma[0]);
257 - $syllable_type = self::CONSONANT_SYLLABLE ;
258 - }
259 - // VOWEL_SYLLABLE Vowel-based syllable
260 - // From OT spec:
261 - else if (preg_match('/^V[S]{0,1}/', substr($s,$ptr), $ma)) {
262 - $syllable_length = strlen($ma[0]);
263 - $syllable_type = self::VOWEL_SYLLABLE ;
264 - }
54 + // BENGALI double-part vowels [09CB 09C7 09BE][09CC 09C7 09D7]
55 + $vstr = str_replace('09CB','09C7 09BE', $vstr); // convert to 2 parts
56 + $vstr = str_replace('09CC','09C7 09D7', $vstr); // 09C7 pre-based is then shifted below
57 + $prebasedvowels = "(09BF|09C7|09C8)";
58 + $nukta = "09BC";
59 + $halant = "09CD";
60 + // mPDF 5.0.044
61 + $bnfullcons = "0995|0996|0997|0998|0999|099A|099B|099C|099D|099F|09A0|09A1|09A2|09A3|09A4|09A5|09A6|09A7|09A8|09AA|09AB|09AC|09AD|09AE|09AF|09B0|09B2|09B6|09B7|09B8|09B9|09DC|09DD|09DF";
62 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
63 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
64 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
65 + // mPDF 5.0.044
66 + // .. and shifting left-based vowel further to the left in case 3 consonants together.
67 + $vstr = preg_replace('/('.$bnfullcons.') '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr);
265 68
266 - for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
267 - $ptr += $syllable_length ;
268 - $syllable_serial++;
269 - if ($syllable_serial == 16) $syllable_serial = 1;
270 - }
271 -}
69 + // mPDF 5.0.044
70 + // If left-based vowel has now been shifted to left of RA/Halant (09B0/09CD)
71 + // Convert here to above-line form (E068) as it would get missed later
72 + // e.g. 09B0 09CD 09AD 09C7 would be changed above =>
73 + // e.g. 09C7 09B0 09CD 09AD. The 09B0 09CD should => E068
74 + // ??? need to add 09BF as well (09BF|09C7|09C8)
75 + $vstr = preg_replace('/(09C7|09C8) 09B0 09CD/', '\\1 E068', $vstr);
272 76
273 -public static function set_syllables_khmer(&$o, $s, &$broken_syllables) {
274 - $ptr = 0;
275 - $syllable_serial = 1;
276 - $broken_syllables = false;
277 -
278 - while($ptr < strlen($s)) {
279 - $match = '';
280 - $syllable_length = 1;
281 - $syllable_type = self::NON_INDIC_CLUSTER ;
282 - // CONSONANT_SYLLABLE Consonant syllable
283 - if (preg_match('/^r?([CR]J?((Z?F)?[N]{0,2})?[ZJ]?G(JN?)?){0,4}[CR]J?((Z?F)?[N]{0,2})?A?((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})?(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
284 - $syllable_length = strlen($ma[0]);
285 - $syllable_type = self::CONSONANT_SYLLABLE ;
286 - }
287 - // VOWEL_SYLLABLE Vowel-based syllable
288 - else if (preg_match('/^(RH|r)?V((Z?F)?[N]{0,2})?(J|([ZJ]?G(JN?)?[CR]J?((Z?F)?[N]{0,2})?){0,4}((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})?(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2})/', substr($s,$ptr), $ma)) {
289 - $syllable_length = strlen($ma[0]);
290 - $syllable_type = self::VOWEL_SYLLABLE ;
291 - }
292 -
293 -
294 - // BROKEN_CLUSTER syllable
295 - else if (preg_match('/^(RH|r)?((Z?F)?[N]{0,2})?(([ZJ]?G(JN?)?)[CR]J?((Z?F)?[N]{0,2})?){0,4}((([ZJ]?G(JN?)?)|GZ)|(GJ)?([ZJ]{0,3}MN?(H|JHJR)?){0,4})(G([CR]J?((Z?F)?[N]{0,2})?|V))?(SZ?)?[v]{0,2}/', substr($s,$ptr), $ma)) {
296 - if (strlen($ma[0])) { // May match blank
297 - $syllable_length = strlen($ma[0]);
298 - $syllable_type = self::BROKEN_CLUSTER ;
299 - $broken_syllables = true;
300 - }
301 - }
302 -
303 - for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
304 - $ptr += $syllable_length ;
305 - $syllable_serial++;
306 - if ($syllable_serial == 16) $syllable_serial = 1;
307 77 }
308 -}
309 78
310 -public static function initial_reordering(&$info, $GSUBdata, $broken_syllables, $indic_config, $scriptblock, $is_old_spec, $dottedcircle) {
311 -
312 - self::update_consonant_positions ($info, $GSUBdata);
313 -
314 - if ($broken_syllables && $dottedcircle) { self::insert_dotted_circles ($info, $dottedcircle); }
315 -
316 - $count = count($info);
317 - if (!$count) return;
318 - $last = 0;
319 - $last_syllable = $info[0]['syllable'];
320 - for ($i = 1; $i < $count; $i++) {
321 - if ($last_syllable != $info[$i]['syllable']) {
322 - self::initial_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
323 - $last = $i;
324 - $last_syllable = $info[$last]['syllable'];
325 - }
79 + // GUJARATI pre-based vowel [0ABF]
80 + else if ($lang=='gu') {
81 + $prebasedvowels = "(0ABF)";
82 + $nukta = "0ABC";
83 + $halant = "0ACD";
84 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
85 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
86 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
326 87 }
327 - self::initial_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
328 -}
329 88
330 -public static function update_consonant_positions(&$info, $GSUBdata) {
331 - $count = count($info);
332 - for ($i = 0; $i < $count; $i++) {
333 - if ($info[$i]['indic_position'] == self::POS_BASE_C) {
334 - $c = $info[$i]['uni'];
335 - // If would substitute...
336 - if (isset($GSUBdata['pref'][$c])) { $info[$i]['indic_position'] = self::POS_POST_C; }
337 - else if (isset($GSUBdata['blwf'][$c])) { $info[$i]['indic_position'] = self::POS_BELOW_C; }
338 - else if (isset($GSUBdata['pstf'][$c])) { $info[$i]['indic_position'] = self::POS_POST_C; }
339 - }
89 + // GURMUKHI/PUNJABI pre-based vowel [0ABF]
90 + else if ($lang=='pa') {
91 + $prebasedvowels = "(0A3F)";
92 + $nukta = "0A3C";
93 + $halant = "0A4D";
94 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
95 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.' '.$nukta.'/','\\2 \\1 '.$nukta, $vstr); // before NUKTA
96 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$prebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
340 97 }
341 -}
342 98
343 -public static function insert_dotted_circles(&$info, $dottedcircle) {
344 - $idx = 0;
345 - $last_syllable = 0;
346 - while ($idx < count($info)) {
347 - $syllable = $info[$idx]['syllable'];
348 - $syllable_type = ($syllable & 0x0F);
349 - if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
350 - $last_syllable = $syllable;
99 + // TAMIL pre-based vowel [0ABF]
100 + else if ($lang=='ta') {
101 + // Shrii (Shree)
102 + $vstr = preg_replace('/0BB6 0BCD 0BB0 0BC0/','E04B', $vstr);
351 103
352 - $dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
353 -
354 - /* Insert dottedcircle after possible Repha. */
355 - while ($idx < count($info) && $last_syllable == $info[$idx]['syllable'] && $info[$idx]['indic_category'] == self::OT_Repha)
356 - $idx++;
357 - array_splice($info, $idx, 0, $dottedcircle);
358 - }
359 - else
360 - $idx++;
104 + // TAMIL double-part vowels [0BCA 0BC6 0BBE][0BCB 0BC7 0BBE][0BCC 0BC6 0BD7]
105 + $vstr = preg_replace('/0BCA/','0BC6 0BBE', $vstr); // convert to 2 parts
106 + $vstr = preg_replace('/0BCB/','0BC7 0BBE', $vstr); // pre-based is then shifted below
107 + $vstr = preg_replace('/0BCC/','0BC6 0BD7', $vstr);
108 + $prebasedvowels = "(0BC6|0BC7|0BC8)";
109 + // No nukta
110 + $halant = "0BCD"; // Doesn't seem to move most in front of halanted consonants
111 + $vstr = preg_replace('/([A-F0-9]{4}) '.$prebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
112 + // ? Only for special case KSS (already moved to left of 0BB7)
113 + $vstr = preg_replace('/0B95 '.$halant.' '.$prebasedvowels.' 0BB7/','\\1 0B95 '.$halant.' 0BB7', $vstr);
361 114 }
362 - // I am not sue how this code below got in here, since $idx should now be > count($info) and thus invalid.
363 - // In case I am missing something(!) I'll leave a warning here for now:
364 - if (isset($info[$idx])) { die("This shouldn't happen (in otl.php)"); exit; }
365 - // In case of final bloken cluster...
366 - //$syllable = $info[$idx]['syllable'];
367 - //$syllable_type = ($syllable & 0x0F);
368 - //if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
369 - // $dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
370 - // array_splice($info, $idx, 0, $dottedcircle);
371 - //}
372 -}
373 115
374 -
375 -
376 -/* Rules from:
377 - * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
378 -
379 -public static function initial_reordering_syllable (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end) {
380 - /* vowel_syllable: We made the vowels look like consonants. So uses the consonant logic! */
381 - /* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */
382 - /* standalone_cluster: We treat NBSP/dotted-circle as if they are consonants, so we should just chain. */
383 -
384 - $syllable_type = ($info[$start]['syllable'] & 0x0F);
385 - if ($syllable_type==self::NON_INDIC_CLUSTER ) { return; }
386 - if ($syllable_type==self::BROKEN_CLUSTER || $syllable_type==self::STANDALONE_CLUSTER ) {
387 - //if ($uniscribe_bug_compatible) {
388 - /* For dotted-circle, this is what Uniscribe does:
389 - * If dotted-circle is the last glyph, it just does nothing.
390 - * i.e. It doesn't form Reph. */
391 - if ($info[$end - 1]['indic_category'] == self::OT_DOTTEDCIRCLE) {
392 - return;
393 - }
116 + // ORIYA
117 + else if ($lang=='or') {
118 + // ORIYA double-part vowels []
119 + $vstr = str_replace('0B48','0B47 0B56', $vstr); // 2-part Vowel
120 + $vstr = str_replace('0B4B','0B47 0B3E', $vstr); // 2-part Vowel
121 + $vstr = str_replace('0B4C','0B47 0B57', $vstr); // 2-part Vowel
122 + $orprebasedvowels = "(0B47)";
123 + // No nukta
124 + $halant = "0B4D";
125 + $vstr = preg_replace('/([A-F0-9]{4}) '.$orprebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
126 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$orprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
127 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$orprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
394 128 }
395 129
396 - /* 1. Find base consonant:
397 - *
398 - * The shaping engine finds the base consonant of the syllable, using the
399 - * following algorithm: starting from the end of the syllable, move backwards
400 - * until a consonant is found that does not have a below-base or post-base
401 - * form (post-base forms have to follow below-base forms), or that is not a
402 - * pre-base reordering Ra, or arrive at the first consonant. The consonant
403 - * stopped at will be the base.
404 - *
405 - * o If the syllable starts with Ra + Halant (in a script that has Reph)
406 - * and has more than one consonant, Ra is excluded from candidates for
407 - * base consonants.
408 - */
130 + // MALAYALAM
131 + else if ($lang=='ml') {
132 + // Chillus - old forms - remove ZWNJ after
133 + // This font Volt rules recognises e.g. "Na Halant(Virama)" as ChilluN
134 + $vstr = preg_replace('/(0D23 0D4D|0D28 0D4D|0D30 0D4D|0D32 0D4D|0D33 0D4D) 200D/','\\1', $vstr);
135 + // See Chillus in Unicode [http://en.wikipedia.org/wiki/Malayalam_script]
136 + $vstr = str_replace('0D7A','0D23 0D4D', $vstr); // [mlymChilluNn]
137 + $vstr = str_replace('0D7B','0D28 0D4D', $vstr); // [mlymChilluN]
138 + $vstr = str_replace('0D7C','0D30 0D4D', $vstr); // [mlymChilluR]
139 + $vstr = str_replace('0D7D','0D32 0D4D', $vstr); // [mlymChilluL]
140 + $vstr = str_replace('0D7E','0D33 0D4D', $vstr); // [mlymChilluLl]
141 +/*
142 + // Chillus - 0D7A-0D7E not in font directly, but as E005-E009
143 + $vstr = preg_replace('/0D23 0D4D 200D/','0D7A', $vstr);
144 + $vstr = preg_replace('/0D28 0D4D 200D/','0D7B', $vstr);
145 + $vstr = preg_replace('/0D30 0D4D 200D/','0D7C', $vstr);
146 + $vstr = preg_replace('/0D32 0D4D 200D/','0D7D', $vstr);
147 + $vstr = preg_replace('/0D33 0D4D 200D/','0D7E', $vstr);
409 148
410 - $base = $end;
411 - $has_reph = false;
412 - $limit = $start;
149 + $vstr = preg_replace('/0D7F/','E004', $vstr); // [mlymChilluK]
150 + $vstr = preg_replace('/0D7A/','E005', $vstr); // [mlymChilluNn]
151 + $vstr = preg_replace('/0D7B/','E006', $vstr); // [mlymChilluN]
152 + $vstr = preg_replace('/0D7C/','E007', $vstr); // [mlymChilluR]
153 + $vstr = preg_replace('/0D7D/','E008', $vstr); // [mlymChilluL]
154 + $vstr = preg_replace('/0D7E/','E009', $vstr); // [mlymChilluLl]
155 +*/
413 156
414 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
415 - /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
416 - * and has more than one consonant, Ra is excluded from candidates for
417 - * base consonants. */
418 - if (count($GSUBdata['rphf']) /* ?? $indic_plan->mask_array[RPHF] */ && $start + 3 <= $end &&
419 - (
420 - ($indic_config[4] == self::REPH_MODE_IMPLICIT && !self::is_joiner($info[$start + 2])) ||
421 - ($indic_config[4] == self::REPH_MODE_EXPLICIT && $info[$start + 2]['indic_category'] == self::OT_ZWJ)
422 - )) {
423 - /* See if it matches the 'rphf' feature. */
424 - //$glyphs = array($info[$start]['uni'], $info[$start + 1]['uni']);
425 - //if ($indic_plan->rphf->would_substitute ($glyphs, count($glyphs), true, face)) {
426 - if (isset($GSUBdata['rphf'][$info[$start]['uni']]) && self::is_halant_or_coeng($info[$start + 1]) ) {
427 - $limit += 2;
428 - while ($limit < $end && self::is_joiner($info[$limit]))
429 - $limit++;
430 - $base = $start;
431 - $has_reph = true;
432 - }
433 - }
434 - else if ($indic_config[4] == self::REPH_MODE_LOG_REPHA && $info[$start]['indic_category'] == self::OT_Repha) {
435 - $limit += 1;
436 - while ($limit < $end && self::is_joiner($info[$limit]))
437 - $limit++;
438 - $base = $start;
439 - $has_reph = true;
440 - }
157 + // MALAYALAM double-part vowels []
158 + $vstr = str_replace('0D4A','0D46 0D3E', $vstr); // 2-part Vowel
159 + $vstr = str_replace('0D4B','0D47 0D3E', $vstr); // 2-part Vowel
160 + $vstr = str_replace('0D4C','0D46 0D57', $vstr); // 2-part Vowel
161 + $mlprebasedvowels = "(0D46|0D47|0D48)";
162 + // No nukta
163 + $halant = "0D4D";
164 + $vstr = preg_replace('/([A-F0-9]{4}) '.$mlprebasedvowels.'/','\\2 \\1', $vstr); // vowel sign pre-based shift left
165 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$mlprebasedvowels.'/','\\2 \\1 '.$halant, $vstr); // before CHAR HALANT
441 166 }
442 167
443 - switch ($indic_config[2]) { // base_pos
444 - case self::BASE_POS_LAST:
445 - /* -> starting from the end of the syllable, move backwards */
446 - $i = $end;
447 - $seen_below = false;
448 - do {
449 - $i--;
450 - /* -> until a consonant is found */
451 - if (self::is_consonant($info[$i])) {
452 - /* -> that does not have a below-base or post-base form
453 - * (post-base forms have to follow below-base forms), */
454 - if ($info[$i]['indic_position'] != self::POS_BELOW_C && ($info[$i]['indic_position'] != self::POS_POST_C || $seen_below)) {
455 - $base = $i;
456 - break;
457 - }
458 - if ($info[$i]['indic_position'] == self::POS_BELOW_C)
459 - $seen_below = true;
460 -
461 - /* -> or that is not a pre-base reordering Ra,
462 - *
463 - * IMPLEMENTATION NOTES:
464 - *
465 - * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
466 - * by the logic above already.
467 - */
468 -
469 - /* -> or arrive at the first consonant. The consonant stopped at will
470 - * be the base. */
471 - $base = $i;
472 - }
473 - else {
474 - /* A ZWJ after a Halant stops the base search, and requests an explicit
475 - * half form.
476 - * [A ZWJ before a Halant, requests a subjoined form instead, and hence
477 - * search continues. This is particularly important for Bengali
478 - * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya] */
479 - if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWJ && $info[$i - 1]['indic_category'] == self::OT_H) {
480 - if (!defined("OMIT_INDIC_FIX_1") || OMIT_INDIC_FIX_1!=1) { $base = $i; } // INDIC_FIX_1
481 - break;
482 - }
483 - // ZKI8
484 - if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWNJ) {
485 - break;
486 - }
487 - }
488 - } while ($i > $limit);
489 - break;
490 -
491 - case self::BASE_POS_FIRST:
492 - /* In scripts without half forms (eg. Khmer), the first consonant is always the base. */
493 -
494 - if (!$has_reph)
495 - $base = $limit;
496 -
497 - /* Find the last base consonant that is not blocked by ZWJ. If there is
498 - * a ZWJ right before a base consonant, that would request a subjoined form. */
499 - for ($i = $limit; $i < $end; $i++) {
500 - if (self::is_consonant($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C) {
501 - if ($limit < $i && $info[$i - 1]['indic_category'] == self::OT_ZWJ)
502 - break;
503 - else
504 - $base = $i;
505 - }
506 - }
507 -
508 - /* Mark all subsequent consonants as below. */
509 - for ($i = $base + 1; $i < $end; $i++) {
510 - if (self::is_consonant ($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C)
511 - $info[$i]['indic_position'] = self::POS_BELOW_C;
512 - }
513 - break;
514 - //default:
515 - //assert (false);
516 - /* fallthrough */
168 + // TELUGU
169 + else if ($lang=='te') {
170 + // TELUGU double-part vowels [0C48 -> 0C46 0C56]
171 + $vstr = str_replace('0C48','0C46 0C56', $vstr); // 2-part Vowel
172 + $prebasedvowels = "(0C46)";
173 + $abvvowels = "(0C3E|0C3F|0C40|0C46|0C47|0C4A|0C4B|0C4C|0C55)";
174 + // No nukta
175 + $halant = "0C4D";
176 + $tefullforms = "0C15|0C17|0C18|0C1A|0C1B|0C1C|0C1D|0C20|0C21|0C22|0C24|0C25|0C26|0C27|0C28|0C2A|0C2B|0C2D|0C2E|0C2F|0C30|0C33|0C35|0C36|0C37|0C38|0C39|E028|E029|E02A|E02B|E078|E07A|E07B";
177 + $vstr = preg_replace('/('.$tefullforms .') '.$halant.' ('.$tefullforms .') '.$abvvowels .'/','\\1 \\3 '.$halant.' \\2', $vstr); // before HALANT
517 178 }
518 179
519 - /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
520 - * and has more than one consonant, Ra is excluded from candidates for
521 - * base consonants.
522 - *
523 - * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
524 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
525 - if ($has_reph && $base == $start && $limit - $base <= 2) {
526 - /* Have no other consonant, so Reph is not formed and Ra becomes base. */
527 - $has_reph = false;
528 - }
529 - }
530 180
531 - /* 2. Decompose and reorder Matras:
532 - *
533 - * Each matra and any syllable modifier sign in the cluster are moved to the
534 - * appropriate position relative to the consonant(s) in the cluster. The
535 - * shaping engine decomposes two- or three-part matras into their constituent
536 - * parts before any repositioning. Matra characters are classified by which
537 - * consonant in a conjunct they have affinity for and are reordered to the
538 - * following positions:
539 - *
540 - * o Before first half form in the syllable
541 - * o After subjoined consonants
542 - * o After post-form consonant
543 - * o After main consonant (for above marks)
544 - *
545 - * IMPLEMENTATION NOTES:
546 - *
547 - * The normalize() routine has already decomposed matras for us, so we don't
548 - * need to worry about that.
549 - */
550 -
551 -
552 - /* 3. Reorder marks to canonical order:
553 - *
554 - * Adjacent nukta and halant or nukta and vedic sign are always repositioned
555 - * if necessary, so that the nukta is first.
556 - *
557 - * IMPLEMENTATION NOTES:
558 - *
559 - * Use the combining Class from Unicode categories? to bubble_sort.
560 - */
561 -
562 - /* Reorder characters */
563 -
564 - for ($i = $start; $i < $base; $i++)
565 - $info[$i]['indic_position'] = min(self::POS_PRE_C, $info[$i]['indic_position']);
566 -
567 - if ($base < $end)
568 - $info[$base]['indic_position'] = self::POS_BASE_C;
569 -
570 - /* Mark final consonants. A final consonant is one appearing after a matra,
571 - * ? only in Khmer. */
572 - for ($i = $base + 1; $i < $end; $i++)
573 - if ($info[$i]['indic_category'] == self::OT_M) {
574 - for ($j = $i + 1; $j < $end; $j++)
575 - if (self::is_consonant ($info[$j])) {
576 - $info[$j]['indic_position'] = self::POS_FINAL_C;
577 - break;
578 - }
579 - break;
580 - }
581 -
582 - /* Handle beginning Ra */
583 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
584 - if ($has_reph)
585 - $info[$start]['indic_position'] = self::POS_RA_TO_BECOME_REPH;
586 - }
587 -
588 -
589 - /* For old-style Indic script tags, move the first post-base Halant after
590 - * last consonant. Only do this if there is *not* a Halant after last
591 - * consonant. Otherwise it becomes messy. */
592 - if ($is_old_spec) {
593 - for ($i = $base + 1; $i < $end; $i++) {
594 - if ($info[$i]['indic_category'] == self::OT_H) {
595 - for ($j = $end - 1; $j > $i; $j--) {
596 - if (self::is_consonant($info[$j]) || $info[$j]['indic_category'] == self::OT_H) { break; }
597 - }
598 - if ($info[$j]['indic_category'] != self::OT_H && $j > $i) {
599 - /* Move Halant to after last consonant. */
600 - self::_move_info_pos($info, $i, $j+1);
601 - }
602 - break;
603 - }
604 - }
181 + // KANNADA
182 + else if ($lang=='kn') {
183 + // KANNADA double-part vowels [0CC8 -> 0CC6 0CD6]
184 + $vstr = str_replace('0CC0','0CBF 0CD5', $vstr); // 2-part Vowel
185 + $vstr = str_replace('0CC7','0CC6 0CD5', $vstr); // 2-part Vowel
186 + $vstr = str_replace('0CC8','0CC6 0CD6', $vstr); // 2-part Vowel AI - no glyph for single
187 + $vstr = str_replace('0CCA','0CC6 0CC2', $vstr); // 2-part Vowel
188 + $vstr = str_replace('0CCB','0CC6 0CC2 0CD5', $vstr); // 2-part Vowel
189 + $prebasedvowels = "(0CBF|0CC6)";
190 + $halant = "0CCD";
605 191 }
606 192
607 - /* Attach misc marks to previous char to move with them. */
608 - $last_pos = self::POS_START;
609 - for ($i = $start; $i < $end; $i++) {
610 - if ((self::FLAG($info[$i]['indic_category']) & (self::FLAG(self::OT_ZWJ)| self::FLAG(self::OT_ZWNJ) | self::FLAG(self::OT_N) | self::FLAG (self::OT_RS) | self::FLAG (self::OT_H) | self::FLAG (self::OT_Coeng) ))) {
611 - $info[$i]['indic_position'] = $last_pos;
612 - if ($info[$i]['indic_category'] == self::OT_H && $info[$i]['indic_position'] == self::POS_PRE_M) {
613 - /*
614 - * Uniscribe doesn't move the Halant with Left Matra.
615 - * TEST: U+092B,U+093F,U+094DE
616 - * We follow. This is important for the Sinhala
617 - * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
618 - * where U+0DD9 is a left matra and U+0DCA is the virama.
619 - * We don't want to move the virama with the left matra.
620 - * TEST: U+0D9A,U+0DDA
621 - */
622 - for ($j = $i; $j > $start; $j--)
623 - if ($info[$j - 1]['indic_position'] != self::POS_PRE_M) {
624 - $info[$i]['indic_position'] = $info[$j - 1]['indic_position'];
625 - break;
626 - }
627 - }
628 - }
629 - else if ($info[$i]['indic_position'] != self::POS_SMVD) {
630 - $last_pos = $info[$i]['indic_position'];
631 - }
632 - }
633 193
634 - /* Re-attach ZWJ, ZWNJ, and halant to next char, for after-base consonants. */
635 - $last_halant = $end;
636 - for ($i = $base + 1; $i < $end; $i++) {
637 - if (self::is_halant_or_coeng($info[$i]))
638 - $last_halant = $i;
639 - else if (self::is_consonant($info[$i])) {
640 - for ($j = $last_halant; $j < $i; $j++)
641 - if ($info[$j]['indic_position'] != self::POS_SMVD)
642 - $info[$j]['indic_position'] = $info[$i]['indic_position'];
643 - }
644 - }
194 + //============================
645 195
196 + // SPECIALS
646 197
647 - if ($scriptblock == UCDN::SCRIPT_KHMER) {
648 - /* KHMER_FIX_2 */
649 - /* Move Coeng+RO (Halant,Ra) sequence before base consonant. */
650 - for ($i = $base + 1; $i < $end; $i++) {
651 - if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni'])) {
652 - $info[$i]['indic_position'] = self::POS_PRE_C;
653 - $info[$i + 1]['indic_position'] = self::POS_PRE_C;
654 - break;
655 - }
656 - }
198 + // DEVANAGARI Ra Halant Ra
199 + if ($lang=='hi') {
200 + $vstr = str_replace('0930 094D 0930','E05D 0930', $vstr); // Ra Halant Ra => halfRa FullRa
657 201 }
658 202
659 -
660 -/*
661 -if (!defined("OMIT_INDIC_FIX_2") || OMIT_INDIC_FIX_2 != 1) {
662 - // INDIC_FIX_2
663 - $ZWNJ_found = false;
664 - $POST_ZWNJ_c_found = false;
665 - for ($i = $base + 1; $i < $end; $i++) {
666 - if ($info[$i]['indic_category'] == self::OT_ZWNJ) { $ZWNJ_found = true; }
667 - else if ($ZWNJ_found && $info[$i]['indic_category'] == self::OT_C) { $POST_ZWNJ_c_found = true; }
668 - else if ($POST_ZWNJ_c_found && $info[$i]['indic_position'] == self::POS_BEFORE_SUB) { $info[$i]['indic_position'] = self::POS_AFTER_SUB; }
203 + // GUJARATI
204 + if ($lang=='gu') {
205 + $vstr = str_replace('0AB0 0AC2','E02E', $vstr); // Ra VowelUu => SpecialForm RaUu
669 206 }
670 -}
671 -*/
672 207
673 - /* Setup masks now */
674 - for ($i = $start; $i < $end; $i++) {
675 - $info[$i]['mask'] = 0;
208 + // TELUGU Ra Halant <Consonant> Halant => halfRa Halant<Consonant> Halant
209 + if ($lang=='te') {
210 + $vstr = preg_replace('/0C30 0C4D ([A-F0-9]{4}) 0C4D/','E021 0C4D \\1 0C4D', $vstr);
676 211 }
677 212
678 -
679 - if ($scriptblock == UCDN::SCRIPT_KHMER) {
680 - /* Find a Coeng+RO (Halant,Ra) sequence and mark it for pre-base processing. */
681 - $mask = self::FLAG(self::PREF);
682 - for ($i = $base; $i < $end-1; $i++) { /* KHMER_FIX_1 From $start (not base) */
683 - if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni']) ) {
684 -
685 - $info[$i]['mask'] |= self::FLAG(self::PREF);
686 - $info[$i + 1]['mask'] |= self::FLAG(self::PREF);
687 -
688 - /* Mark the subsequent stuff with 'cfar'. Used in Khmer.
689 - * Read the feature spec.
690 - * This allows distinguishing the following cases with MS Khmer fonts:
691 - * U+1784,U+17D2,U+179A,U+17D2,U+1782 [C+Coeng+RO+Coeng+C] => Should activate CFAR
692 - * U+1784,U+17D2,U+1782,U+17D2,U+179A [C+Coeng+C+Coeng+RO] => Should NOT activate CFAR
693 - */
694 - for ($j=($i+2); $j < $end; $j++)
695 - $info[$j]['mask'] |= self::FLAG(self::CFAR);
696 -
697 - break;
698 - }
699 - }
213 + // KANNADA
214 + // Reph at end of word becomes E0CC instead of E00B
215 + if ($lang=='kn') {
216 + $vstr = str_replace('0CB0 0CCD 0020','E0CC 0020', $vstr); // mPDF 5.3.87
700 217 }
701 218
702 219
220 + //============================
221 + // MAIN BIT FROM VOLT RULES
222 + foreach($voltdata[$font] AS $rid=>$reps) {
223 +//echo $rid . ': ' . $vstr.'<br />';
224 + $vstr = preg_replace('/'.$reps['match'].'/',$reps['replace'], $vstr);
225 + }
226 +//echo $vstr.'<br />'; exit;
703 227
704 - /* Sit tight, rock 'n roll! */
705 - self::bubble_sort ($info, $start, $end - $start);
706 228
707 - /* Find base again */
708 - $base = $end;
709 - for ($i = $start; $i < $end; $i++) {
710 - if ($info[$i]['indic_position'] == self::POS_BASE_C) {
711 - $base = $i;
712 - break;
713 - }
714 - }
229 + //============================
715 230
716 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
717 - /* Reph */
718 - for ($i = $start; $i < $end; $i++) {
719 - if ($info[$i]['indic_position'] == self::POS_RA_TO_BECOME_REPH) {
720 - $info[$i]['mask'] |= self::FLAG(self::RPHF);
721 - }
722 - }
231 + // SPECIALS
723 232
724 - /* Pre-base */
725 - $mask = self::FLAG(self::HALF);
726 - for ($i = $start; $i < $base; $i++) {
727 - $info[$i]['mask'] |= $mask;
233 + // KANNADA
234 + // <Base> <BelowBase1> [<BelowBase2> ] MatraI -> <Base/MatraI ligature> <Belowbase1> etc
235 + if ($lang=='kn') {
236 + $matraI = "0CBF";
237 + $knbase = preg_split('/\|/', "0C95|0C96|0C97|0C98|0C9A|0C9B|0C9C|0C9D|0CA0|0CA1|0CA2|0CA3|0CA4|0CA5|0CA6|0CA7|0CA8|0CAA|0CAB|0CAC|0CAD|0CAE|0CAF|0CB0|0CB2|0CB3|0CB5|0CB6|0CB7|0CB8|0CB9|E0A3|E07D|E07E");
238 + $knmatraIligature = preg_split('/\|/', "E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A4|E0A1|E0A2");
239 + $belowbase1 = "E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E081";
240 + $belowbase2 = "E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E081";
241 + for ($i=0; $i<count($knbase);$i++) {
242 + $vstr = preg_replace('/'.$knbase[$i].' ('.$belowbase1.') ('.$belowbase2.') '.$matraI.'/', $knmatraIligature[$i].' \\1 \\2', $vstr);
243 + $vstr = preg_replace('/'.$knbase[$i].' ('.$belowbase1.') '.$matraI.'/', $knmatraIligature[$i].' \\1', $vstr);
728 244 }
729 245 }
730 246
731 - /* Post-base */
732 - $mask = (self::FLAG(self::BLWF) | self::FLAG(self::ABVF) | self::FLAG(self::PSTF));
733 - for ($i = $base + 1; $i < $end; $i++) {
734 - $info[$i]['mask'] |= $mask;
247 + // KANNADA
248 + // [KanTtaFull] [matraI] => [KanTtaPartial] [matraI]
249 + if ($lang=='kn') {
250 + $vstr = preg_replace('/0C9F '.$matraI.'/', 'E015 '.$matraI, $vstr);
735 251 }
736 252
737 -
738 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
739 - if (!defined("OMIT_INDIC_FIX_3") || OMIT_INDIC_FIX_3 != 1) {
740 - /* INDIC_FIX_3 */
741 - /* Find a (pre-base) Consonant, Halant,Ra sequence and mark Halant|Ra for below-base BLWF processing. */
742 - // TEST CASE &#x995;&#x9cd;&#x9b0;&#x9cd;&#x995; in FreeSans versus Vrinda
743 - if (($base - $start) >= 3) {
744 - for ($i = $start; $i < ($base-2); $i++) {
745 - if (self::is_consonant($info[$i])) {
746 - if (self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i + 2]['uni'])) {
747 - // If would substitute Halant+Ra...BLWF
748 - if (isset($GSUBdata['blwf'][$info[$i+2]['uni']])) {
749 - $info[$i + 1]['mask'] |= self::FLAG(self::BLWF);
750 - $info[$i + 2]['mask'] |= self::FLAG(self::BLWF);
751 - }
752 - /* If would not substitute as blwf, mark Ra+Halant for RPHF using following Halant (if present) */
753 - else if (self::is_halant_or_coeng($info[$i + 3])) {
754 - $info[$i + 2]['mask'] |= self::FLAG(self::RPHF);
755 - $info[$i + 3]['mask'] |= self::FLAG(self::RPHF);
756 - }
757 - break;
758 - }
759 - }
760 - }
761 - }
253 + // ORIYA
254 + if ($lang=='or') {
255 + // SpecialCase Ra[0B30] Halant still left before [oryaFullNnNna] => E00F
256 + $vstr = preg_replace('/0B30 '.$halant.' E00F/','E00F E069', $vstr); // convert to Reph
762 257 }
763 - }
764 258
259 + //============================
260 + // SHIFT REPH
765 261
262 + // DEVANAGARI Shift Reph [E015]
263 + if ($lang=='hi') {
264 + // FIRSTLY - halfRa = E05D - Change this to Reph [E015]
265 + $himatchhalfforms = "E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5|E0F6|E0F7|E0F8|E0F9|E0FA|E0FB|E0FC|E0FD|E0FE|E0FF|E100|E101|E102|E103|E104|E105|E106|E107|E108|E109|E10A|E10B|E10C|E10D|E10E|E10F|E110|E111|E112|E113|E114|E115|E116|E117|E118|E119|E11A|E13D|E13E|E13F|E140|E141|E142|E143|E144|E145";
266 + $himatchfullforms = "0915|0916|0917|0918|0919|091A|091B|091C|091D|091E|091F|0920|0921|0922|0923|0924|0925|0926|0927|0928|092A|092B|092C|092D|092E|092F|0930|0932|0933|0935|0936|0937|0938|0939|E028|E029|0958|0959|095A|E02A|E02B|E02C|E02D|095B|E02E|E02F|E030|E031|095C|095D|E032|E033|E034|E035|E036|0929|E037|095E|E038|E039|E03A|095F|0931|E03B|0934|E03C|E03D|E03E|E03F|E040|E041|E042|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E11E|E11F|E120|E121|E122|E123|E124|E125|E126|E127|E128|E129|E12A|E12B|E12C|E12D|E12E|E12F|E130|E131|E132|E133";
267 + $vstr = preg_replace('/E05D ('.$himatchhalfforms.'|'.$himatchfullforms.')/', 'E015 \\1', $vstr);
766 268
767 - if ($is_old_spec && $scriptblock == UCDN::SCRIPT_DEVANAGARI) {
768 - /* Old-spec eye-lash Ra needs special handling. From the spec:
769 - * "The feature 'below-base form' is applied to consonants
770 - * having below-base forms and following the base consonant.
771 - * The exception is vattu, which may appear below half forms
772 - * as well as below the base glyph. The feature 'below-base
773 - * form' will be applied to all such occurrences of Ra as well."
774 - *
775 - * Test case: U+0924,U+094D,U+0930,U+094d,U+0915
776 - * with Sanskrit 2003 font.
777 - *
778 - * However, note that Ra,Halant,ZWJ is the correct way to
779 - * request eyelash form of Ra, so we wouldbn't inhibit it
780 - * in that sequence.
781 - *
782 - * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
783 - */
784 - for ($i = $start; ($i + 1) < $base; $i++) {
785 - if ($info[$i]['indic_category'] == self::OT_Ra && $info[$i+1]['indic_category'] == self::OT_H &&
786 - ($i + 2 == $base || $info[$i+2]['indic_category'] != self::OT_ZWJ)) {
787 - $info[$i]['mask'] |= self::FLAG(self::BLWF);
788 - $info[$i+1]['mask'] |= self::FLAG(self::BLWF);
789 - }
269 + // Reph = E015 - Shift Right to just after end of syllable
270 + // FullAllForms + HalfAllForms + 093E matraA
271 + while(preg_match('/E015 ('.$himatchhalfforms.')/', $vstr)) {
272 + $vstr = preg_replace('/E015 ('.$himatchhalfforms.')/', '\\1 E015', $vstr);
790 273 }
791 - }
274 + $vstr = preg_replace('/E015 ('.$himatchfullforms.')/', '\\1 E015', $vstr);
792 275
793 - if ($scriptblock != UCDN::SCRIPT_KHMER) {
794 - if (count($GSUBdata['pref']) && $base + 2 < $end) {
795 - /* Find a Halant,Ra sequence and mark it for pre-base processing. */
796 - for ($i = $base + 1; $i + 1 < $end; $i++) {
797 - // If old_spec find Ra-Halant...
798 - if ((isset($GSUBdata['pref'][$info[$i + 1]['uni']]) && self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni']) ) ||
799 - ($is_old_spec && isset($GSUBdata['pref'][$info[$i]['uni']]) && self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i]['uni']) )
800 - ) {
801 - $info[$i++]['mask'] |= self::FLAG(self::PREF);
802 - $info[$i++]['mask'] |= self::FLAG(self::PREF);
803 - break;
804 - }
805 - }
806 - }
276 + // Now shift it beyond post-based vowels // ??? Need to add others e.g. 0949,094A,094B,094C + presentation forms like E198
277 + $vstr = str_replace('E015 093E', '093E E015', $vstr);
278 + $vstr = preg_replace('/E015 (0940|E194|E195|E196|E197|E198)/', '\\1 E014', $vstr); // (Small) reph [E014] to Right of matraI
279 + $vstr = str_replace('E015 0947', '0947 E014', $vstr); // (Small) reph [E014] to Right of matraI
807 280 }
808 281
809 -
810 - /* Apply ZWJ/ZWNJ effects */
811 - for ($i = $start + 1; $i < $end; $i++) {
812 - if (self::is_joiner ($info[$i])) {
813 - $non_joiner = ($info[$i]['indic_category'] == self::OT_ZWNJ);
814 - $j = $i;
815 - while ($j > $start) {
816 - if (defined("OMIT_INDIC_FIX_4") && OMIT_INDIC_FIX_4 == 1) {
817 - // INDIC_FIX_4 = do nothing - carry on //
818 - // ZWNJ should block H C from forming blwf post-base - need to unmask backwards beyond first consonant arrived at //
819 - if (!self::is_consonant($info[$j])) { break; }
820 - }
821 - $j--;
822 -
823 - /* ZWJ/ZWNJ should disable CJCT. They do that by simply
824 - * being there, since we don't skip them for the CJCT
825 - * feature (ie. F_MANUAL_ZWJ) */
826 -
827 - /* A ZWNJ disables HALF. */
828 - if ($non_joiner) {
829 - $info[$j]['mask'] &= ~(self::FLAG(self::HALF) | self::FLAG(self::BLWF));
830 - }
831 -
832 - }
833 - }
282 + // BENGALI Shift Reph [E068]
283 + else if ($lang=='bn') {
284 + $bnfullconjuncts = "E002|E003|E004|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5|E0F6|E0F7|E0F8|E0F9|E0FA|E0FB|E0FC|E0FD|E0FE|E0FF|E100|E101|E102|E103|E104|E105|E106|E107|E108|E109|E10A|E10B|E10C|E10D|E10E|E10F|E110|E111|E112|E113|E114|E115|E116|E117|E118|E119|E11A|E11B|E11C|E11D|E11E|E11F|E120|E121|E122|E123|E124|E125|E126|E127|E128|E129|E12A|E12B|E12C|E12D|E12E|E12F|E130|E131|E132|E133|E134|E135|E136|E137|E138|E139|E13A|E13B|E13C|E13D|E13E|E13F|E140|E141|E142|E143|E144|E145|E146|E147|E148|E149|E14A|E14B|E14C|E14D|E14E|E14F|E150|E151|E152|E153|E154|E155|E156|E157|E158|E159|E15A|E15B|E15C|E15D|E15E|E15F|E160|E161|E162|E163|E164|E165|E166|E167|E168|E169|E16A|E16B|E16C|E16D|E16E|E16F|E170|E171|E172|E173|E174|E175|E176|E177|E178|E179|E17A|E17B|E17C|E17D|E17E|E17F|E180|E181|E182|E183|E184|E185|E186|E187|E188|E189|E18A|E18B|E18C|E18D|E18E|E18F|E190|E191|E192|E193|E194|E195|E196|E197|E198|E199|E19A";
285 + // $bnfullcons - set above;
286 + $vstr = preg_replace('/E068 ('.$bnfullconjuncts.'|'.$bnfullcons.')/', '\\1 E068', $vstr);
287 + // ? Need to shift it beyond post-base vowels 09BE, 09C0, 09D7 haven't found so can't test??
288 + $vstr = preg_replace('/E068 (09BE|09C0|09D7)/', '\\1 E068', $vstr);
834 289 }
835 -}
836 290
837 -public static function final_reordering (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec) {
838 - $count = count($info);
839 - if (!$count) return;
840 - $last = 0;
841 - $last_syllable = $info[0]['syllable'];
842 - for ($i = 1; $i < $count; $i++) {
843 - if ($last_syllable != $info[$i]['syllable']) {
844 - self::final_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
845 - $last = $i;
846 - $last_syllable = $info[$last]['syllable'];
847 - }
291 + // GUJARATI Shift Reph [E032]
292 + else if ($lang=='gu') {
293 + $gufullforms = "0A95|0A96|0A97|0A98|0A99|0A9A|0A9B|0A9C|0A9D|0A9E|0A9F|0AA0|0AA1|0AA2|0AA3|0AA4|0AA5|0AA6|0AA7|0AA8|0AAA|0AAB|0AAC|0AAD|0AAE|0AAF|0AB0|0AB2|0AB3|0AB5|0AB6|0AB7|0AB8|0AB9|E002|E003|E004|E005|E006|E007|E008|E009|E00A|E00B|E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D|E07E|E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5";
294 + $vstr = preg_replace('/E032 ('.$gufullforms.')/', '\\1 E032', $vstr);
295 + // Now shift it beyond post-based vowels // ??? Need to add others e.g. 0949,094A,094B,094C + presentation forms like E198
296 + // ? Need to shift it beyond post-base vowels 0ABE, 0AC0 haven't found so can't test??
297 + $vstr = preg_replace('/E032 (0ABE|0AC0)/', '\\1 E032', $vstr);
848 298 }
849 - self::final_reordering_syllable ($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
850 299
851 -}
852 300
853 -public static function final_reordering_syllable (&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end) {
854 -
855 - /* 4. Final reordering:
856 - *
857 - * After the localized forms and basic shaping forms GSUB features have been
858 - * applied (see below), the shaping engine performs some final glyph
859 - * reordering before applying all the remaining font features to the entire
860 - * cluster.
861 - */
862 -
863 - /* Find base again */
864 - for ($base = $start; $base < $end; $base++)
865 - if ($info[$base]['indic_position'] >= self::POS_BASE_C) {
866 - if ($start < $base && $info[$base]['indic_position'] > self::POS_BASE_C)
867 - $base--;
868 - break;
301 + // TELUGU Shift Reph to LEFT [E046|E069|E077] [TelRaSmallOne] => E046 [TelRaSmallTwo] => E069 [TelRaSmallThree] => E077
302 + else if ($lang=='te') {
303 + // tefullforms defined earlier
304 + $tepartialforms = "E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E07C|E07D|E07E";
305 + $matraligs = "E07F|E080|E081|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF";
306 + $tevowels = "0C3E|0C3F|0C40|0C46|0C47|0C56|0C4A|0C4B|0C4C"
307 + ."|0C41|0C42|0C43|0C44"; // post matras
308 + $vstr = preg_replace('/('.$tevowels.') (E046|E069|E077)/', '\\2 \\1', $vstr);
309 + while(preg_match('/('.$tepartialforms.') (E046|E069|E077)/', $vstr)) {
310 + $vstr = preg_replace('/('.$tepartialforms.') (E046|E069|E077)/', '\\2 \\1', $vstr);
869 311 }
870 - if ($base == $end && $start < $base && $info[$base - 1]['indic_category'] != self::OT_ZWJ)
871 - $base--;
872 - while ($start < $base && isset($info[$base]) && ($info[$base]['indic_category'] == self::OT_H || $info[$base]['indic_category'] == self::OT_N))
873 - $base--;
312 + $vstr = preg_replace('/('.$tefullforms .'|'.$matraligs.') (E046|E069|E077)/', '\\2 \\1', $vstr);
313 + }
874 314
875 315
876 - /* o Reorder matras:
877 - *
878 - * If a pre-base matra character had been reordered before applying basic
879 - * features, the glyph can be moved closer to the main consonant based on
880 - * whether half-forms had been formed. Actual position for the matra is
881 - * defined as "after last standalone halant glyph, after initial matra
882 - * position and before the main consonant". If ZWJ or ZWNJ follow this
883 - * halant, position is moved after it.
884 - */
885 -
886 -
887 - if ($start + 1 < $end && $start < $base) { /* Otherwise there can't be any pre-base matra characters. */
888 - /* If we lost track of base, alas, position before last thingy. */
889 - $new_pos = ($base == $end) ? $base - 2 : $base - 1;
890 -
891 - /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
892 - * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
893 - * We want to position matra after them.
894 - */
895 - if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
896 - while ($new_pos > $start && !(self::is_one_of ($info[$new_pos], (self::FLAG(self::OT_M) | self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng)))))
897 - $new_pos--;
898 -
899 - /* If we found no Halant we are done.
900 - * Otherwise only proceed if the Halant does
901 - * not belong to the Matra itself! */
902 - if (self::is_halant_or_coeng($info[$new_pos]) && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
903 - /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
904 - if ($new_pos + 1 < $end && self::is_joiner($info[$new_pos + 1]))
905 - $new_pos++;
906 - }
907 - else
908 - $new_pos = $start; /* No move. */
316 + // KANNADA Shift Reph to RIGHT [E00B]
317 + else if ($lang=='kn') {
318 + $knfullforms = "0C95|0C96|0C97|0C98|0C99|0C9A|0C9B|0C9C|0C9D|0C9E|0C9F|0CA0|0CA1|0CA2|0CA3|0CA4|0CA5|0CA6|0CA7|0CA8|0CAA|0CAB|0CAC|0CAD|0CAE|0CAF|0CB0|0CB1|0CB2|0CB3|0CB5|0CB6|0CB7|0CB8|0CB9|E07D|E07E|E0A3";
319 + $knpartialforms = "E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|0C9E|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E07F";
320 + while(preg_match('/E00B ('.$knpartialforms.')/', $vstr)) {
321 + $vstr = preg_replace('/E00B ('.$knpartialforms.')/', '\\1 E00B', $vstr);
909 322 }
323 + // mPDF 5.3.47 Also move Reph to right of matraIligatures
324 + $knfullforms .= "|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A4|E0A1|E0A2";
325 + $vstr = preg_replace('/E00B ('.$knfullforms.')/', '\\1 E00B', $vstr);
910 326
911 - if ($start < $new_pos && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
912 - /* Now go see if there's actually any matras... */
913 - for ($i = $new_pos; $i > $start; $i--)
914 - if ($info[$i - 1]['indic_position'] == self::POS_PRE_M) {
915 - $old_pos = $i - 1;
916 - //memmove (&info[$old_pos], &info[$old_pos + 1], ($new_pos - $old_pos) * sizeof ($info[0]));
917 - self::_move_info_pos($info, $old_pos, $new_pos+1);
918 -
919 - if ($old_pos < $base && $base <= $new_pos) /* Shouldn't actually happen. */
920 - $base--;
921 - $new_pos--;
922 - }
923 - }
327 + // ? Need to shift it beyond base or below-base forms - haven't found so can't test??
328 + // mPDF 5.3.87
329 + // E004 added to list (which is a transformed version of 0CBE)
330 + $knvowels = "0CBE|0CC0|0CC1|0CC2|0CC3|0CC4|0CC7|0CC8|0CCA|0CCB|0CD5|0CD6|E004";
331 + $vstr = preg_replace('/E00B ('.$knvowels.')/', '\\1 E00B', $vstr);
924 332 }
925 333
926 334
927 - /* o Reorder reph:
928 - *
929 - * Reph's original position is always at the beginning of the syllable,
930 - * (i.e. it is not reordered at the character reordering stage). However,
931 - * it will be reordered according to the basic-forms shaping results.
932 - * Possible positions for reph, depending on the script, are; after main,
933 - * before post-base consonant forms, and after post-base consonant forms.
934 - */
335 + // ORIYA Shift Reph to RIGHT [E069|E06A|E06B|E06C]
336 + else if ($lang=='or') {
337 + $orrephs = "E069|E06A|E06B|E06C";
338 + $orfullforms = "0B15|0B16|0B17|0B18|0B19|0B1A|0B1B|0B1C|0B1D|0B1E|0B1F|0B20|0B21|0B22|0B23|0B24|0B25|0B26|0B27|0B28|0B29|0B2A|0B2B|0B2C|0B2D|0B2E|0B2F|0B30|0B31|0B32|0B33|0B34|0B35|0B36|0B37|0B38|E003|E004|E005|E006|E007|E008|E009|E00A|E00B|E00C|E00D|E00E|E00F|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037";
339 + // E123 - E147 FullHalant forms ? add to FullForms
340 + $orpartialforms = "E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB|E0AC|E0AD|E0AE|E0AF|E0B0|E0B1|E0B2|E0B3|E0B4|E0B5|E0B6|E0B7|E0B8|E0B9|E0BA|E0BB|E0BC|E0BD|E0BE|E0BF|E0C0|E0C1|E0C2|E0C3|E0C4|E0C5|E0C6|E0C7|E0C8|E0C9|E0CA|E0CB|E0CC|E0CD|E0CE|E0CF|E0D0|E0D1|E0D2|E0D3|E0D4|E0D5|E0D6|E0D7|E0D8|E0D9|E0DA|E0DB|E0DC|E0DD|E0DE|E0DF|E0E0|E0E1|E0E2|E0E3|E0E4|E0E5|E0E6|E0E7|E0E8|E0E9|E0EA|E0EB|E0EC|E0ED|E0EE|E0EF|E0F0|E0F1|E0F2|E0F3|E0F4|E0F5";
935 341
936 - /* If there's anything after the Ra that has the REPH pos, it ought to be halant.
937 - * Which means that the font has failed to ligate the Reph. In which case, we
938 - * shouldn't move. */
939 - if ($start + 1 < $end &&
940 - $info[$start]['indic_position'] == self::POS_RA_TO_BECOME_REPH && $info[$start + 1]['indic_position'] != self::POS_RA_TO_BECOME_REPH) {
941 - $reph_pos = $indic_config[3];
942 - $skip_to_reph_step_5 = false;
943 - $skip_to_reph_move = false;
342 + // Combined MatraIReph[E06D] split [0B3F & E069] to allow reph to be shifted forwards
343 + $vstr = preg_replace('/('.$orfullforms.') E06D ('.$orfullforms.') 0B3E/', '\\1 0B3F E069 \\2 0B3E', $vstr);
944 344
945 - /* 1. If reph should be positioned after post-base consonant forms,
946 - * proceed to step 5.
947 - */
948 - if ($reph_pos == self::REPH_POS_AFTER_POST) {
949 - $skip_to_reph_step_5 = true;
950 - }
951 345
952 - /* 2. If the reph repositioning class is not after post-base: target
953 - * position is after the first explicit halant glyph between the
954 - * first post-reph consonant and last main consonant. If ZWJ or ZWNJ
955 - * are following this halant, position is moved after it. If such
956 - * position is found, this is the target position. Otherwise,
957 - * proceed to the next step.
958 - *
959 - * Note: in old-implementation fonts, where classifications were
960 - * fixed in shaping engine, there was no case where reph position
961 - * will be found on this step.
962 - */
963 -
964 - if (!$skip_to_reph_step_5) {
965 -
966 - $new_reph_pos = $start + 1;
967 -
968 - while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
969 - $new_reph_pos++;
970 -
971 - if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
972 - /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
973 - if ($new_reph_pos + 1 < $base && self::is_joiner ($info[$new_reph_pos + 1]))
974 - $new_reph_pos++;
975 - $skip_to_reph_move =true;
976 - }
346 + while(preg_match('/('.$orrephs.') ('.$orpartialforms.')/', $vstr)) {
347 + $vstr = preg_replace('/('.$orrephs.') ('.$orpartialforms.')/', '\\2 \\1', $vstr);
977 348 }
349 + $vstr = preg_replace('/('.$orrephs.') ('.$orfullforms.')/', '\\2 \\1', $vstr);
978 350
979 - /* 3. If reph should be repositioned after the main consonant: find the
980 - * first consonant not ligated with main, or find the first
981 - * consonant that is not a potential pre-base reordering Ra.
982 - */
983 - if ($reph_pos == self::REPH_POS_AFTER_MAIN && !$skip_to_reph_move && !$skip_to_reph_step_5) {
984 - $new_reph_pos = $base;
985 - /* XXX Skip potential pre-base reordering Ra. */
986 - while ($new_reph_pos + 1 < $end && $info[$new_reph_pos + 1]['indic_position'] <= self::POS_AFTER_MAIN)
987 - $new_reph_pos++;
988 - if ($new_reph_pos < $end)
989 - $skip_to_reph_move =true;
990 - }
991 351
992 - /* 4. If reph should be positioned before post-base consonant, find
993 - * first post-base classified consonant not ligated with main. If no
994 - * consonant is found, the target position should be before the
995 - * first matra, syllable modifier sign or vedic sign.
996 - */
997 - /* This is our take on what step 4 is trying to say (and failing, BADLY). */
998 - if ($reph_pos == self::REPH_POS_AFTER_SUB && !$skip_to_reph_move && !$skip_to_reph_step_5) {
999 - $new_reph_pos = $base;
1000 - while ($new_reph_pos < $end && isset($info[$new_reph_pos + 1]['indic_position']) &&
1001 - !( self::FLAG($info[$new_reph_pos + 1]['indic_position']) & (self::FLAG(self::POS_POST_C) | self::FLAG(self::POS_AFTER_POST) | self::FLAG(self::POS_SMVD)))) {
1002 - $new_reph_pos++;
1003 - }
1004 - if ($new_reph_pos < $end) { $skip_to_reph_move =true; }
1005 - }
352 + // Combine Reph and MatraI
353 + $vstr = str_replace('E069 0B3F', 'E06D', $vstr); // Reph and MatraI -> MatraIReph
354 + $vstr = str_replace('E06A 0B3F', 'E06E', $vstr); // Reph and MatraI -> MatraIReph
355 + $vstr = str_replace('E06B 0B3F', 'E06F', $vstr); // Reph and MatraI -> MatraIReph
356 + }
1006 357
1007 - /* 5. If no consonant is found in steps 3 or 4, move reph to a position
1008 - * immediately before the first post-base matra, syllable modifier
1009 - * sign or vedic sign that has a reordering class after the intended
1010 - * reph position. For example, if the reordering position for reph
1011 - * is post-main, it will skip above-base matras that also have a
1012 - * post-main position.
1013 - */
1014 - if (!$skip_to_reph_move) {
1015 - /* Copied from step 2. */
1016 - $new_reph_pos = $start + 1;
1017 - while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
1018 - $new_reph_pos++;
1019 358
1020 - if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
1021 - /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
1022 - if ($new_reph_pos + 1 < $base && self::is_joiner($info[$new_reph_pos + 1]))
1023 - $new_reph_pos++;
1024 - $skip_to_reph_move =true;
1025 - }
1026 - }
359 + // MALAYALAM Shift Reph to LEFT [E00E] (mlylmRaVattu)
360 + else if ($lang=='ml') {
361 + $halant = "0D4D";
362 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' 0D30/','E00E \\1', $vstr); // 0D30 = Ra
363 + $vstr = preg_replace('/([A-F0-9]{4}) '.$halant.' '.$mlprebasedvowels .' 0D30/','\\2 E00E \\1', $vstr); // 0D30 = Ra
1027 364
365 + $mlfullforms = "0D15|0D16|0D17|0D18|0D19|0D1A|0D1B|0D1C|0D1D|0D1E|0D1F|0D20|0D21|0D22|0D23|0D24|0D25|0D26|0D27|0D28|0D2A|0D2B|0D2C|0D2D|0D2E|0D2F|0D30|0D31|0D32|0D33|0D34|0D35|0D36|0D37|0D38|0D39"
366 + ."|E010|E011|E012|E013|E014|E015|E016|E017|E018|E019|E01A|E01B|E01C|E01D|E01E|E01F|E020|E021|E022|E023|E024|E025|E026|E027|E028|E029|E02A|E02B|E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E|E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071|E072|E073|E074|E075|E076|E077|E078|E079|E07A|E07B|E07C|E07D";
367 + // = FullConsonants + FullConjuncts
1028 368
1029 - /* 6. Otherwise, reorder reph to the end of the syllable.
1030 - */
1031 - if (!$skip_to_reph_move) {
1032 - $new_reph_pos = $end - 1;
1033 - while ($new_reph_pos > $start && $info[$new_reph_pos]['indic_position'] == self::POS_SMVD)
1034 - $new_reph_pos--;
369 + // = Add Chillu characters // mPDF 5.0.024
370 + $mlfullforms .= "|E004|E005|E006|E007|E008|E009";
371 + while(preg_match('/('.$mlfullforms.') E00E/', $vstr))
372 + $vstr = preg_replace('/('.$mlfullforms.') E00E/', 'E00E \\1', $vstr);
373 + }
1035 374
1036 - /*
1037 - * If the Reph is to be ending up after a Matra,Halant sequence,
1038 - * position it before that Halant so it can interact with the Matra.
1039 - * However, if it's a plain Consonant,Halant we shouldn't do that.
1040 - * Uniscribe doesn't do this.
1041 - * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
1042 - */
1043 - //if (!$hb_options.uniscribe_bug_compatible && self::is_halant_or_coeng($info[$new_reph_pos])) {
1044 - if (self::is_halant_or_coeng($info[$new_reph_pos])) {
1045 - for ($i = $base + 1; $i < $new_reph_pos; $i++)
1046 - if ($info[$i]['indic_category'] == self::OT_M) {
1047 - /* Ok, got it. */
1048 - $new_reph_pos--;
1049 - }
1050 - }
1051 - }
375 + //============================
1052 376
377 + // SHIFT post-based vowels to Left of SmallForms (NOT to left of full forms)
1053 378
1054 - /* Move */
1055 - self::_move_info_pos($info, $start, $new_reph_pos+1);
1056 -
1057 - if ($start < $base && $base <= $new_reph_pos) {
1058 - $base--;
1059 - }
379 + // TELUGU Shift
380 + if ($lang=='te') {
381 + // NB $tevowels defined above
382 + // NB $tefullforms defined above
383 + $tebelowbase1 = "E02C|E02D|E02E|E02F|E030|E031|E032|E033|E034|E035|E036|E037|E038|E039|E03A|E03B|E03C|E03D|E03E|E03F|E040|E041|E042|E043|E044|E045|E046|E047|E048|E049|E04A|E04B|E04C|E04D|E04E"; //'Small1KaToHa'
384 + $tebelowbase2 = "E04F|E050|E051|E052|E053|E054|E055|E056|E057|E058|E059|E05A|E05B|E05C|E05D|E05E|E05F|E060|E061|E062|E063|E064|E065|E066|E067|E068|E069|E06A|E06B|E06C|E06D|E06E|E06F|E070|E071"; // 'Small2KaToHa'
385 + $vstr = preg_replace('/('.$tebelowbase2.') ('.$tevowels.')/', '\\2 \\1', $vstr);
386 + $vstr = preg_replace('/('.$tebelowbase1.') ('.$tevowels.')/', '\\2 \\1', $vstr);
1060 387 }
1061 388
1062 389
1063 - /* o Reorder pre-base reordering consonants:
1064 - *
1065 - * If a pre-base reordering consonant is found, reorder it according to
1066 - * the following rules:
1067 - */
390 + // KANNADA Shift
391 + else if ($lang=='kn') {
392 + $knvowels = "0CBE|0CC0|0CC1|0CC2|0CC3|0CC4|0CC7|0CC8|0CCA|0CCB|0CD5|0CD6"
393 + // mPDF 5.3.87 Shouldn't swop E082 and E047 (belowbase1) below
394 + // E082 is a matraIligature
395 + // ."|E082|E083|E084|E085|E086|E087|E088|E089|E08A|E08B|E08C|E08D|E08E|E08F|E090|E091|E092|E093|E094|E095|E096|E097|E098|E099|E09A|E09B|E09C|E09D|E09E|E09F|E0A0|E0A1|E0A2|E0A3|E0A4|E0A5|E0A6|E0A7|E0A8|E0A9|E0AA|E0AB"
396 + ."|E004|E007|E008|E009|E00A";
1068 397
1069 398
1070 - if (count($GSUBdata['pref']) && $base + 1 < $end) { /* Otherwise there can't be any pre-base reordering Ra. */
1071 - for ($i = $base + 1; $i < $end; $i++) {
1072 - if ($info[$i]['mask'] & self::FLAG(self::PREF)) {
1073 - /* 1. Only reorder a glyph produced by substitution during application
1074 - * of the <pref> feature. (Note that a font may shape a Ra consonant with
1075 - * the feature generally but block it in certain contexts.)
1076 - */
1077 -// ??? Need to TEST if actual substitution has occurred
1078 - if ($i + 1 == $end || ($info[$i + 1]['mask'] & self::FLAG(self::PREF)) == 0) {
1079 - /*
1080 - * 2. Try to find a target position the same way as for pre-base matra.
1081 - * If it is found, reorder pre-base consonant glyph.
1082 - *
1083 - * 3. If position is not found, reorder immediately before main
1084 - * consonant.
1085 - */
1086 - $new_pos = $base;
1087 - /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
1088 - * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
1089 - * We want to position matra after them.
1090 - */
1091 - if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
1092 - while ($new_pos > $start &&
1093 - !(self::is_one_of($info[$new_pos - 1], self::FLAG(self::OT_M) | self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng))))
1094 - $new_pos--;
399 + // NB $knvowels defined above
400 + // NB $fullforms defined above
401 + // $belowbase1/2 defined above
402 + $vstr = preg_replace('/('.$belowbase2.') ('.$knvowels.')/', '\\2 \\1', $vstr);
403 + // mPDF 5.3.87
404 + $vstr = preg_replace('/('.$belowbase1.') ('.$knvowels.')/', '\\2 \\1', $vstr);
1095 405
1096 - /* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a
1097 - * split matra, it should be reordered to *before* the left part of such matra. */
1098 - if ($new_pos > $start && $info[$new_pos - 1]['indic_category'] == self::OT_M) {
1099 - $old_pos = i;
1100 - for ($i = $base + 1; $i < $old_pos; $i++)
1101 - if ($info[$i]['indic_category'] == self::OT_M) {
1102 - $new_pos--;
1103 - break;
1104 - }
1105 - }
1106 - }
1107 -
1108 - if ($new_pos > $start && self::is_halant_or_coeng($info[$new_pos - 1])) {
1109 - /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
1110 - if ($new_pos < $end && self::is_joiner($info[$new_pos]))
1111 - $new_pos++;
1112 - }
1113 -
1114 - $old_pos = $i;
1115 - self::_move_info_pos($info, $old_pos, $new_pos);
1116 -
1117 - if ($new_pos <= $base && $base < $old_pos)
1118 - $base++;
1119 - }
1120 -
1121 - break;
1122 - }
1123 - }
406 + //$vstr = preg_replace('/('.$fullforms.') ('.$knvowels.')/', '\\2 \\1', $vstr);
1124 407 }
1125 408
1126 -
1127 - /* Apply 'init' to the Left Matra if it's a word start. */
1128 - if ($info[$start]['indic_position'] == self::POS_PRE_M &&
1129 - ($start==0 ||
1130 - ($info[$start - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_FORMAT || $info[$start - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
1131 - )) {
1132 - $info[$start]['mask'] |= self::FLAG(self::INIT);
409 + //============================
410 + // Clear unwanted ZWJ, ZWNJ
411 + // MALAYALAM
412 + if ($lang=='ml') {
413 + $vstr = preg_replace('/(200C|200D) /','', $vstr);
1133 414 }
1134 415
416 + //============================
417 + // END & PUT IT BACK TOGETHER
418 + $vstr = preg_replace('/^0020 (.*) 0020$/', '\\1', $vstr);
1135 419
1136 - /*
1137 - * Finish off and go home!
1138 - */
1139 -
1140 -}
1141 -
1142 -function _move_info_pos(&$info, $from, $to) {
1143 - $t = array();
1144 - $t[0] = $info[$from];
1145 - if ($from > $to) {
1146 - array_splice($info, $from, 1);
1147 - array_splice($info, $to, 0, $t);
420 + $varr = explode(" ",$vstr);
421 + $e = '';
422 + foreach($varr AS $v) {
423 + $e.=code2utf(hexdec($v));
1148 424 }
1149 - else {
1150 - array_splice($info, $to, 0, $t);
1151 - array_splice($info, $from, 1);
1152 - }
1153 -}
425 + //============================
1154 426
1155 -
1156 -public static $ra_chars = array(
1157 - 0x0930 => 1, /* Devanagari */
1158 - 0x09B0 => 1, /* Bengali */
1159 - 0x09F0 => 1, /* Bengali (Assamese) */
1160 - 0x0A30 => 1, /* Gurmukhi */ /* No Reph */
1161 - 0x0AB0 => 1, /* Gujarati */
1162 - 0x0B30 => 1, /* Oriya */
1163 - 0x0BB0 => 1, /* Tamil */ /* No Reph */
1164 - 0x0C30 => 1, /* Telugu */ /* Reph formed only with ZWJ */
1165 - 0x0CB0 => 1, /* Kannada */
1166 - 0x0D30 => 1, /* Malayalam */ /* No Reph, Logical Repha */
1167 -
1168 - 0x0DBB => 1, /* Sinhala */ /* Reph formed only with ZWJ */
1169 - 0x179A => 1, /* Khmer */ /* No Reph, Visual Repha */
1170 -);
1171 -
1172 -public static function is_ra ($u) {
1173 - if (isset(self::$ra_chars[$u])) return true;
1174 - return false;
427 + return $e;
1175 428 }
1176 429
1177 -public static function is_one_of ($info, $flags) {
1178 - if (isset($info['is_ligature']) && $info['is_ligature']) return false; /* If it ligated, all bets are off. */
1179 - return !!(self::FLAG($info['indic_category']) & $flags);
1180 -}
1181 430
1182 -public static function is_joiner($info) {
1183 - return self::is_one_of ($info, (self::FLAG(self::OT_ZWJ) | self::FLAG(self::OT_ZWNJ)));
1184 431 }
1185 -
1186 -
1187 -/* Vowels and placeholders treated as if they were consonants. */
1188 -public static function is_consonant($info) {
1189 - return self::is_one_of($info, (self::FLAG(self::OT_C) | self::FLAG(self::OT_CM) | self::FLAG(self::OT_Ra) | self::FLAG(self::OT_V) | self::FLAG(self::OT_NBSP) | self::FLAG(self::OT_DOTTEDCIRCLE)));
1190 -}
1191 -
1192 -
1193 -public static function is_halant_or_coeng($info) {
1194 - return self::is_one_of($info, (self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng)));
1195 -}
1196 -
1197 -
1198 -
1199 -// From hb-private.hh
1200 -public static function in_range ($u, $lo, $hi) {
1201 - if ( (($lo^$hi) & $lo) == 0 && (($lo^$hi) & $hi) == ($lo^$hi) && (($lo^$hi) & (($lo^$hi) + 1)) == 0 )
1202 - return ($u & ~($lo^$hi)) == $lo;
1203 - else
1204 - return $lo <= $u && $u <= $hi;
1205 -}
1206 -// From hb-private.hh
1207 -public static function FLAG($x) { return (1<<($x)); }
1208 -
1209 -
1210 -// BELOW from hb-ot-shape-complex-indic.cc
1211 -
1212 -/*
1213 - * Indic configurations.
1214 - */
1215 -
1216 -// base_position
1217 -const BASE_POS_FIRST = 0;
1218 -const BASE_POS_LAST = 1;
1219 -
1220 -// reph_position
1221 -const REPH_POS_DEFAULT = 10; // POS_BEFORE_POST,
1222 -
1223 -const REPH_POS_AFTER_MAIN = 5; // POS_AFTER_MAIN,
1224 -const REPH_POS_BEFORE_SUB = 7; // POS_BEFORE_SUB,
1225 -const REPH_POS_AFTER_SUB = 9; // POS_AFTER_SUB,
1226 -const REPH_POS_BEFORE_POST = 10; // POS_BEFORE_POST,
1227 -const REPH_POS_AFTER_POST = 12; // POS_AFTER_POST
1228 -
1229 -// reph_mode
1230 -const REPH_MODE_IMPLICIT = 0; /* Reph formed out of initial Ra,H sequence. */
1231 -const REPH_MODE_EXPLICIT = 1; /* Reph formed out of initial Ra,H,ZWJ sequence. */
1232 -const REPH_MODE_VIS_REPHA = 2; /* Encoded Repha character, no reordering needed. */
1233 -const REPH_MODE_LOG_REPHA = 3; /* Encoded Repha character, needs reordering. */
1234 -
1235 -
1236 -
1237 -/*
1238 -struct of indic_configs{
1239 - KEY - script;
1240 - 0 - has_old_spec;
1241 - 1 - virama;
1242 - 2 - base_pos;
1243 - 3 - reph_pos;
1244 - 4 - reph_mode;
1245 -};
1246 -*/
1247 -
1248 -public static $indic_configs = array( /* index is SCRIPT_number from UCDN */
1249 - 9 => array(true, 0x094D, 1, 10, 0),
1250 - 10 => array(true, 0x09CD, 1, 9, 0),
1251 - 11 => array(true, 0x0A4D, 1, 7, 0),
1252 - 12 => array(true, 0x0ACD, 1, 10, 0),
1253 - 13 => array(true, 0x0B4D, 1, 5, 0),
1254 - 14 => array(true, 0x0BCD, 1, 12, 0),
1255 - 15 => array(true, 0x0C4D, 1, 12, 1),
1256 - 16 => array(true, 0x0CCD, 1, 12, 0),
1257 - 17 => array(true, 0x0D4D, 1, 5, 3),
1258 - 18 => array(false, 0x0DCA, 0, 5, 1), /* Sinhala */
1259 - 30 => array(false, 0x17D2, 0, 10, 2), /* Khmer */
1260 - 84 => array(false, 0xA9C0, 1, 10, 0), /* Javanese */
1261 -
1262 -);
1263 -
1264 -
1265 -
1266 -/*
1267 -
1268 -// from "hb-ot-shape-complex-indic-table.cc"
1269 -
1270 -
1271 -const ISC_A = 0; // INDIC_SYLLABIC_CATEGORY_AVAGRAHA Avagraha
1272 -const ISC_Bi = 8; // INDIC_SYLLABIC_CATEGORY_BINDU Bindu
1273 -const ISC_C = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT Consonant
1274 -const ISC_CD = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD Consonant_Dead
1275 -const ISC_CF = 17; // INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL Consonant_Final
1276 -const ISC_CHL = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER Consonant_Head_Letter
1277 -const ISC_CM = 17; // INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL Consonant_Medial
1278 -const ISC_CP = 11; // INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER Consonant_Placeholder
1279 -const ISC_CR = 15; // INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA Consonant_Repha
1280 -const ISC_CS = 1; // INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED Consonant_Subjoined
1281 -const ISC_ML = 0; // INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER Modifying_Letter
1282 -const ISC_N = 3; // INDIC_SYLLABIC_CATEGORY_NUKTA Nukta
1283 -const ISC_x = 0; // INDIC_SYLLABIC_CATEGORY_OTHER Other
1284 -const ISC_RS = 13; // INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER Register_Shifter
1285 -const ISC_TL = 0; // INDIC_SYLLABIC_CATEGORY_TONE_LETTER Tone_Letter
1286 -const ISC_TM = 3; // INDIC_SYLLABIC_CATEGORY_TONE_MARK Tone_Mark
1287 -const ISC_V = 4; // INDIC_SYLLABIC_CATEGORY_VIRAMA Virama
1288 -const ISC_Vs = 8; // INDIC_SYLLABIC_CATEGORY_VISARGA Visarga
1289 -const ISC_Vo = 2; // INDIC_SYLLABIC_CATEGORY_VOWEL Vowel
1290 -const ISC_M = 7; // INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT Vowel_Dependent
1291 -const ISC_VI = 2; // INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT Vowel_Independent
1292 -
1293 -const IMC_B = 8; // INDIC_MATRA_CATEGORY_BOTTOM Bottom
1294 -const IMC_BR = 11; // INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT Bottom_And_Right
1295 -const IMC_I = 15; // INDIC_MATRA_CATEGORY_INVISIBLE Invisible
1296 -const IMC_L = 3; // INDIC_MATRA_CATEGORY_LEFT Left
1297 -const IMC_LR = 11; // INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT Left_And_Right
1298 -const IMC_x = 15; // INDIC_MATRA_CATEGORY_NOT_APPLICABLE Not_Applicable
1299 -const IMC_O = 5; // INDIC_MATRA_CATEGORY_OVERSTRUCK Overstruck
1300 -const IMC_R = 11; // INDIC_MATRA_CATEGORY_RIGHT Right
1301 -const IMC_T = 6; // INDIC_MATRA_CATEGORY_TOP Top
1302 -const IMC_TB = 8; // INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM Top_And_Bottom
1303 -const IMC_TBR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT Top_And_Bottom_And_Right
1304 -const IMC_TL = 6; // INDIC_MATRA_CATEGORY_TOP_AND_LEFT Top_And_Left
1305 -const IMC_TLR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT Top_And_Left_And_Right
1306 -const IMC_TR = 11; // INDIC_MATRA_CATEGORY_TOP_AND_RIGHT Top_And_Right
1307 -const IMC_VOL = 2; // INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT Visual_Order_Left
1308 -
1309 -If in original table = _(C,x), that = ISC_C,IMC_x
1310 -Value is IMC_x << 8 (or IMC_x * 256) = 3840
1311 -plus ISC_C = 1, so = 3841
1312 -
1313 - */
1314 -
1315 -
1316 -
1317 -public static $indic_table = array(
1318 -
1319 - /* Devanagari (0900..097F) */
1320 -
1321 - /* 0900 */ 3848,3848,3848,3848,3842,3842,3842,3842,
1322 - /* 0908 */ 3842,3842,3842,3842,3842,3842,3842,3842,
1323 - /* 0910 */ 3842,3842,3842,3842,3842, 3841, 3841, 3841,
1324 - /* 0918 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1325 - /* 0920 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1326 - /* 0928 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1327 - /* 0930 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1328 - /* 0938 */ 3841, 3841, 1543, 2823, 3843, 3840, 2823, 775,
1329 - /* 0940 */ 2823, 2055, 2055, 2055, 2055, 1543, 1543, 1543,
1330 - /* 0948 */ 1543, 2823, 2823, 2823, 2823, 2052, 775, 2823,
1331 - /* 0950 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 2055,
1332 - /* 0958 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1333 - /* 0960 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1334 - /* 0968 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1335 - /* 0970 */ 3840, 3840,3842,3842,3842,3842,3842,3842,
1336 - /* 0978 */ 3840, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1337 -
1338 - /* Bengali (0980..09FF) */
1339 -
1340 - /* 0980 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
1341 - /* 0988 */ 3842,3842,3842,3842,3842, 3840, 3840,3842,
1342 - /* 0990 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
1343 - /* 0998 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1344 - /* 09A0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1345 - /* 09A8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1346 - /* 09B0 */ 3841, 3840, 3841, 3840, 3840, 3840, 3841, 3841,
1347 - /* 09B8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
1348 - /* 09C0 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
1349 - /* 09C8 */ 775, 3840, 3840,2823,2823, 2052,3841, 3840,
1350 - /* 09D0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
1351 - /* 09D8 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
1352 - /* 09E0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1353 - /* 09E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1354 - /* 09F0 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
1355 - /* 09F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1356 -
1357 - /* Gurmukhi (0A00..0A7F) */
1358 -
1359 - /* 0A00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
1360 - /* 0A08 */ 3842,3842,3842, 3840, 3840, 3840, 3840,3842,
1361 - /* 0A10 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
1362 - /* 0A18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1363 - /* 0A20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1364 - /* 0A28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1365 - /* 0A30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3840,
1366 - /* 0A38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
1367 - /* 0A40 */ 2823, 2055, 2055, 3840, 3840, 3840, 3840, 1543,
1368 - /* 0A48 */ 1543, 3840, 3840, 1543, 1543, 2052, 3840, 3840,
1369 - /* 0A50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1370 - /* 0A58 */ 3840, 3841, 3841, 3841, 3841, 3840, 3841, 3840,
1371 - /* 0A60 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1372 - /* 0A68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1373 - /* 0A70 */ 3848, 3840,13841,13841, 3840, 3857, 3840, 3840,
1374 - /* 0A78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1375 -
1376 - /* Gujarati (0A80..0AFF) */
1377 -
1378 - /* 0A80 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
1379 - /* 0A88 */ 3842,3842,3842,3842,3842,3842, 3840,3842,
1380 - /* 0A90 */ 3842,3842, 3840,3842,3842, 3841, 3841, 3841,
1381 - /* 0A98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1382 - /* 0AA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1383 - /* 0AA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1384 - /* 0AB0 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
1385 - /* 0AB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
1386 - /* 0AC0 */ 2823, 2055, 2055, 2055, 2055, 1543, 3840, 1543,
1387 - /* 0AC8 */ 1543,2823, 3840, 2823, 2823, 2052, 3840, 3840,
1388 - /* 0AD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1389 - /* 0AD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1390 - /* 0AE0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1391 - /* 0AE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1392 - /* 0AF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1393 - /* 0AF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1394 -
1395 - /* Oriya (0B00..0B7F) */
1396 -
1397 - /* 0B00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
1398 - /* 0B08 */ 3842,3842,3842,3842,3842, 3840, 3840,3842,
1399 - /* 0B10 */ 3842, 3840, 3840,3842,3842, 3841, 3841, 3841,
1400 - /* 0B18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1401 - /* 0B20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1402 - /* 0B28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1403 - /* 0B30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
1404 - /* 0B38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
1405 - /* 0B40 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
1406 - /* 0B48 */ 1543, 3840, 3840,2823,2823,2052, 3840, 3840,
1407 - /* 0B50 */ 3840, 3840, 3840, 3840, 3840, 3840, 1543,2823,
1408 - /* 0B58 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
1409 - /* 0B60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1410 - /* 0B68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1411 - /* 0B70 */ 3840, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
1412 - /* 0B78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1413 -
1414 - /* Tamil (0B80..0BFF) */
1415 -
1416 - /* 0B80 */ 3840, 3840, 3848, 3840, 3840, 3842, 3842, 3842,
1417 - /* 0B88 */ 3842, 3842, 3842, 3840, 3840, 3840, 3842,3842,
1418 - /* 0B90 */ 3842, 3840, 3842, 3842, 3842, 3841, 3840, 3840,
1419 - /* 0B98 */ 3840, 3841, 3841, 3840, 3841, 3840, 3841, 3841,
1420 - /* 0BA0 */ 3840, 3840, 3840, 3841, 3841, 3840, 3840, 3840,
1421 - /* 0BA8 */ 3841, 3841, 3841, 3840, 3840, 3840, 3841, 3841,
1422 - /* 0BB0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1423 - /* 0BB8 */ 3841, 3841, 3840, 3840, 3840, 3840, 2823, 2823,
1424 - /* 0BC0 */ 1543, 2055, 2055, 3840, 3840, 3840, 775, 775,
1425 - /* 0BC8 */ 775, 3840, 2823, 2823, 2823, 1540, 3840, 3840,
1426 - /* 0BD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
1427 - /* 0BD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1428 - /* 0BE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1429 - /* 0BE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1430 - /* 0BF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1431 - /* 0BF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1432 -
1433 - /* Telugu (0C00..0C7F) */
1434 -
1435 - /* 0C00 */ 3840,3848,3848,3848, 3840,3842,3842,3842,
1436 - /* 0C08 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
1437 - /* 0C10 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
1438 - /* 0C18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1439 - /* 0C20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1440 - /* 0C28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1441 - /* 0C30 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
1442 - /* 0C38 */ 3841, 3841, 3840, 3840, 3840, 3840, 1543, 1543,
1443 - /* 0C40 */ 1543, 2823, 2823, 2823, 2823, 3840, 1543, 1543,
1444 - /* 0C48 */ 2055, 3840, 1543, 1543, 1543, 1540, 3840, 3840,
1445 - /* 0C50 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 3840,
1446 - /* 0C58 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
1447 - /* 0C60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1448 - /* 0C68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1449 - /* 0C70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1450 - /* 0C78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1451 -
1452 - /* Kannada (0C80..0CFF) */
1453 -
1454 - /* 0C80 */ 3840, 3840,3848,3848, 3840,3842,3842,3842,
1455 - /* 0C88 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
1456 - /* 0C90 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
1457 - /* 0C98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1458 - /* 0CA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1459 - /* 0CA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1460 - /* 0CB0 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
1461 - /* 0CB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
1462 - /* 0CC0 */ 2823, 2823, 2823, 2823, 2823, 3840, 1543,2823,
1463 - /* 0CC8 */ 2823, 3840,2823,2823, 1543, 1540, 3840, 3840,
1464 - /* 0CD0 */ 3840, 3840, 3840, 3840, 3840, 2823, 2823, 3840,
1465 - /* 0CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3841, 3840,
1466 - /* 0CE0 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1467 - /* 0CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1468 - /* 0CF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1469 - /* 0CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1470 -
1471 - /* Malayalam (0D00..0D7F) */
1472 -
1473 - /* 0D00 */ 3840, 3840,3848,3848, 3840,3842,3842,3842,
1474 - /* 0D08 */ 3842,3842,3842,3842,3842, 3840,3842,3842,
1475 - /* 0D10 */ 3842, 3840,3842,3842,3842, 3841, 3841, 3841,
1476 - /* 0D18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1477 - /* 0D20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1478 - /* 0D28 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1479 - /* 0D30 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1480 - /* 0D38 */ 3841, 3841, 3841, 3840, 3840, 3840, 2823, 2823,
1481 - /* 0D40 */ 2823, 2823, 2823, 2055, 2055, 3840, 775, 775,
1482 - /* 0D48 */ 775, 3840,2823,2823,2823, 1540, 3855, 3840,
1483 - /* 0D50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
1484 - /* 0D58 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1485 - /* 0D60 */ 3842,3842, 2055, 2055, 3840, 3840, 3840, 3840,
1486 - /* 0D68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1487 - /* 0D70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1488 - /* 0D78 */ 3840, 3840,3841,3841,3841,3841,3841,3841,
1489 -
1490 - /* Sinhala (0D80..0DFF) */
1491 -
1492 - /* 0D80 */ 3840, 3840, 3848, 3848, 3840, 3842, 3842, 3842,
1493 - /* 0D88 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
1494 - /* 0D90 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3840,
1495 - /* 0D98 */ 3840, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
1496 - /* 0DA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1497 - /* 0DA8 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1498 - /* 0DB0 */ 3841, 3841, 3840, 3841, 3841, 3841, 3841, 3841,
1499 - /* 0DB8 */ 3841, 3841, 3841, 3841, 3840, 3841, 3840, 3840,
1500 - /* 0DC0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3840,
1501 - /* 0DC8 */ 3840, 3840, 1540, 3840, 3840, 3840, 3840, 2823,
1502 - /* 0DD0 */ 2823, 2823, 1543, 1543, 2055, 3840, 2055, 3840,
1503 - /* 0DD8 */ 2823, 775, 1543, 775, 2823, 2823, 2823, 2823,
1504 - /* 0DE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1505 - /* 0DE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1506 - /* 0DF0 */ 3840, 3840, 2823, 2823, 3840, 3840, 3840, 3840,
1507 - /* 0DF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1508 -
1509 -
1510 - /* Vedic Extensions (1CD0..1CFF) */
1511 -
1512 - /* 1CD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1513 - /* 1CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1514 - /* 1CE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1515 - /* 1CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1516 - /* 1CF0 */ 3840, 3840,3848,3848, 3840, 3840, 3840, 3840,
1517 - /* 1CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1518 -
1519 -
1520 -);
1521 -
1522 -public static $khmer_table = array(
1523 -
1524 - /* Khmer (1780..17FF) */
1525 -
1526 - /* 1780 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1527 - /* 1788 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1528 - /* 1790 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1529 - /* 1798 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
1530 - /* 17A0 */ 3841, 3841, 3841, 3842, 3842, 3842, 3842, 3842,
1531 - /* 17A8 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
1532 - /* 17B0 */ 3842, 3842, 3842, 3842, 3840, 3840, 2823, 1543,
1533 - /* 17B8 */ 1543, 1543, 1543, 2055, 2055, 2055, 1543,2823,
1534 - /* 17C0 */ 2823, 775, 775, 775, 2823, 2823, 3848, 3848,
1535 - /* 17C8 */ 2823, 3853, 3853, 3840, 3855, 3840, 3840, 3840,
1536 - /* 17D0 */ 3840, 1540, 3844, 3840, 3840, 3840, 3840, 3840,
1537 - /* 17D8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1538 - /* 17E0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1539 - /* 17E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1540 - /* 17F0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1541 - /* 17F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
1542 -
1543 -
1544 -);
1545 -
1546 -
1547 -
1548 -// from "hb-ot-shape-complex-indic-table.cc"
1549 -public static function indic_get_categories ($u) {
1550 - if (0x0900 <= $u && $u <= 0x0DFF) return self::$indic_table[$u - 0x0900 + 0]; // offset 0 for Most "indic"
1551 - if (0x1CD0 <= $u && $u <= 0x1D00) return self::$indic_table[$u - 0x1CD0 + 1152]; // offset for Vedic extensions
1552 - if (0x1780 <= $u && $u <= 0x17FF) return self::$khmer_table[$u - 0x1780]; // Khmer
1553 - if ($u == 0x00A0) return 3851; // (ISC_CP | (IMC_x << 8))
1554 - if ($u == 0x25CC) return 3851; // (ISC_CP | (IMC_x << 8))
1555 - return 3840; // (ISC_x | (IMC_x << 8))
1556 -}
1557 -
1558 -// BELOW from hb-ot-shape-complex-indic.cc
1559 -/*
1560 - * Indic shaper.
1561 - */
1562 -
1563 -public static function IN_HALF_BLOCK($u, $Base) { return (($u & ~0x7F) == $Base); }
1564 -
1565 -public static function IS_DEVA($u) { return self::IN_HALF_BLOCK ($u, 0x0900); }
1566 -public static function IS_BENG($u) { return self::IN_HALF_BLOCK ($u, 0x0980); }
1567 -public static function IS_GURU($u) { return self::IN_HALF_BLOCK ($u, 0x0A00); }
1568 -public static function IS_GUJR($u) { return self::IN_HALF_BLOCK ($u, 0x0A80); }
1569 -public static function IS_ORYA($u) { return self::IN_HALF_BLOCK ($u, 0x0B00); }
1570 -public static function IS_TAML($u) { return self::IN_HALF_BLOCK ($u, 0x0B80); }
1571 -public static function IS_TELU($u) { return self::IN_HALF_BLOCK ($u, 0x0C00); }
1572 -public static function IS_KNDA($u) { return self::IN_HALF_BLOCK ($u, 0x0C80); }
1573 -public static function IS_MLYM($u) { return self::IN_HALF_BLOCK ($u, 0x0D00); }
1574 -public static function IS_SINH($u) { return self::IN_HALF_BLOCK ($u, 0x0D80); }
1575 -public static function IS_KHMR($u) { return self::IN_HALF_BLOCK ($u, 0x1780); }
1576 -
1577 -
1578 -public static function MATRA_POS_LEFT($u) { return self::POS_PRE_M; }
1579 -public static function MATRA_POS_RIGHT($u) { return
1580 - (self::IS_DEVA($u) ? self::POS_AFTER_SUB :
1581 - (self::IS_BENG($u) ? self::POS_AFTER_POST :
1582 - (self::IS_GURU($u) ? self::POS_AFTER_POST :
1583 - (self::IS_GUJR($u) ? self::POS_AFTER_POST :
1584 - (self::IS_ORYA($u) ? self::POS_AFTER_POST :
1585 - (self::IS_TAML($u) ? self::POS_AFTER_POST :
1586 - (self::IS_TELU($u) ? ($u <= 0x0C42 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
1587 - (self::IS_KNDA($u) ? ($u < 0x0CC3 || $u > 0xCD6 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
1588 - (self::IS_MLYM($u) ? self::POS_AFTER_POST :
1589 - (self::IS_SINH($u) ? self::POS_AFTER_SUB :
1590 - (self::IS_KHMR($u) ? self::POS_AFTER_POST :
1591 - self::POS_AFTER_SUB))))))))))); /*default*/
1592 - }
1593 -public static function MATRA_POS_TOP($u) { return /* BENG and MLYM don't have top matras. */
1594 - (self::IS_DEVA($u) ? self::POS_AFTER_SUB :
1595 - (self::IS_GURU($u) ? self::POS_AFTER_POST : /* Deviate from spec */
1596 - (self::IS_GUJR($u) ? self::POS_AFTER_SUB :
1597 - (self::IS_ORYA($u) ? self::POS_AFTER_MAIN :
1598 - (self::IS_TAML($u) ? self::POS_AFTER_SUB :
1599 - (self::IS_TELU($u) ? self::POS_BEFORE_SUB :
1600 - (self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
1601 - (self::IS_SINH($u) ? self::POS_AFTER_SUB :
1602 - (self::IS_KHMR($u) ? self::POS_AFTER_POST :
1603 - self::POS_AFTER_SUB))))))))); /*default*/
1604 - }
1605 -public static function MATRA_POS_BOTTOM($u) { return
1606 - (self::IS_DEVA($u) ? self::POS_AFTER_SUB :
1607 - (self::IS_BENG($u) ? self::POS_AFTER_SUB :
1608 - (self::IS_GURU($u) ? self::POS_AFTER_POST :
1609 - (self::IS_GUJR($u) ? self::POS_AFTER_POST :
1610 - (self::IS_ORYA($u) ? self::POS_AFTER_SUB :
1611 - (self::IS_TAML($u) ? self::POS_AFTER_POST :
1612 - (self::IS_TELU($u) ? self::POS_BEFORE_SUB :
1613 - (self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
1614 - (self::IS_MLYM($u) ? self::POS_AFTER_POST :
1615 - (self::IS_SINH($u) ? self::POS_AFTER_SUB :
1616 - (self::IS_KHMR($u) ? self::POS_AFTER_POST :
1617 - self::POS_AFTER_SUB))))))))))); /*default*/
1618 - }
1619 -
1620 -public static function matra_position ($u, $side) {
1621 - switch ($side) {
1622 - case self::POS_PRE_C: return self::MATRA_POS_LEFT($u);
1623 - case self::POS_POST_C: return self::MATRA_POS_RIGHT($u);
1624 - case self::POS_ABOVE_C: return self::MATRA_POS_TOP($u);
1625 - case self::POS_BELOW_C: return self::MATRA_POS_BOTTOM($u);
1626 - }
1627 - return $side;
1628 -}
1629 -
1630 -// vowel matras that have to be split into two parts.
1631 -// From Harfbuzz (old)
1632 -// New HarfBuzz uses /src/hb-ucdn/ucdn.c and unicodedata_db.h for full method of decomposition for all characters
1633 -// Should always fully decompose and then recompose back, but we will just do the split matras
1634 -public static function decompose_indic($ab) {
1635 - $sub = array();
1636 - switch ($ab) {
1637 - /*
1638 - * Decompose split matras.
1639 - */
1640 - /* bengali */
1641 - case 0x9cb : $sub[0] = 0x9c7; $sub[1]= 0x9be; return $sub;
1642 - case 0x9cc : $sub[0] = 0x9c7; $sub[1]= 0x9d7; return $sub;
1643 - /* oriya */
1644 - case 0xb48 : $sub[0] = 0xb47; $sub[1]= 0xb56; return $sub;
1645 - case 0xb4b : $sub[0] = 0xb47; $sub[1]= 0xb3e; return $sub;
1646 - case 0xb4c : $sub[0] = 0xb47; $sub[1]= 0xb57; return $sub;
1647 - /* tamil */
1648 - case 0xbca : $sub[0] = 0xbc6; $sub[1]= 0xbbe; return $sub;
1649 - case 0xbcb : $sub[0] = 0xbc7; $sub[1]= 0xbbe; return $sub;
1650 - case 0xbcc : $sub[0] = 0xbc6; $sub[1]= 0xbd7; return $sub;
1651 - /* telugu */
1652 - case 0xc48 : $sub[0] = 0xc46; $sub[1]= 0xc56; return $sub;
1653 - /* kannada */
1654 - case 0xcc0 : $sub[0] = 0xcbf; $sub[1]= 0xcd5; return $sub;
1655 - case 0xcc7 : $sub[0] = 0xcc6; $sub[1]= 0xcd5; return $sub;
1656 - case 0xcc8 : $sub[0] = 0xcc6; $sub[1]= 0xcd6; return $sub;
1657 - case 0xcca : $sub[0] = 0xcc6; $sub[1]= 0xcc2; return $sub;
1658 - case 0xccb : $sub[0] = 0xcc6; $sub[1]= 0xcc2; $sub[2]= 0xcd5; return $sub;
1659 - /* malayalam */
1660 - case 0xd4a : $sub[0] = 0xd46; $sub[1]= 0xd3e; return $sub;
1661 - case 0xd4b : $sub[0] = 0xd47; $sub[1]= 0xd3e; return $sub;
1662 - case 0xd4c : $sub[0] = 0xd46; $sub[1]= 0xd57; return $sub;
1663 - /* sinhala */
1664 - // NB Some fonts break with these Sinhala decomps (although this is Uniscribe spec)
1665 - // Can check if character would be substituted by pstf and only decompose if true
1666 - // e.g. if (isset($GSUBdata['pstf'][$ab])) - would need to pass $GSUBdata as parameter to this function
1667 - case 0xdda : $sub[0] = 0xdd9; $sub[1]= 0xdca; return $sub;
1668 - case 0xddc : $sub[0] = 0xdd9; $sub[1]= 0xdcf; return $sub;
1669 - case 0xddd : $sub[0] = 0xdd9; $sub[1]= 0xdcf; $sub[2]= 0xdca; return $sub;
1670 - case 0xdde : $sub[0] = 0xdd9; $sub[1]= 0xddf; return $sub;
1671 - /* khmer */
1672 - case 0x17be : $sub[0] = 0x17c1; $sub[1]= 0x17be; return $sub;
1673 - case 0x17bf : $sub[0] = 0x17c1; $sub[1]= 0x17bf; return $sub;
1674 - case 0x17c0 : $sub[0] = 0x17c1; $sub[1]= 0x17c0; return $sub;
1675 -
1676 - case 0x17c4 : $sub[0] = 0x17c1; $sub[1]= 0x17c4; return $sub;
1677 - case 0x17c5 : $sub[0] = 0x17c1; $sub[1]= 0x17c5; return $sub;
1678 - /* tibetan - included here although does not use Inidc shaper in other ways */
1679 - case 0xf73 : $sub[0] = 0xf71; $sub[1]= 0xf72; return $sub;
1680 - case 0xf75 : $sub[0] = 0xf71; $sub[1]= 0xf74; return $sub;
1681 - case 0xf76 : $sub[0] = 0xfb2; $sub[1]= 0xf80; return $sub;
1682 - case 0xf77 : $sub[0] = 0xfb2; $sub[1]= 0xf81; return $sub;
1683 - case 0xf78 : $sub[0] = 0xfb3; $sub[1]= 0xf80; return $sub;
1684 - case 0xf79 : $sub[0] = 0xfb3; $sub[1]= 0xf71; $sub[2]= 0xf80; return $sub;
1685 - case 0xf81 : $sub[0] = 0xf71; $sub[1]= 0xf80; return $sub;
1686 - }
1687 - return false;
1688 -}
1689 -
1690 -
1691 -
1692 -
1693 -
1694 -public static function bubble_sort(&$arr, $start, $len) {
1695 - if ($len<2) { return;}
1696 - $k = $start+$len-2;
1697 - while ($k >= $start) {
1698 - for ($j=$start; $j<=$k; $j++) {
1699 - if ($arr[$j]['indic_position'] > $arr[$j + 1]['indic_position']) {
1700 - $t = $arr[$j];
1701 - $arr[$j] = $arr[$j + 1];
1702 - $arr[$j + 1] = $t;
1703 - }
1704 - }
1705 - $k--;
1706 - }
1707 -}
1708 -
1709 -
1710 -
1711 -
1712 -} // end Class
1713 432
1714 433 ?>