����JFIFXX�����    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222����"��4�� ���,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu���#ډb���_�N�?��wQ���5-�~�I���8����TK<5o�Iv-�����k�_U_�����~b�M��d����Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F�?_�'ϔ��_�Ջt��=||I ��6�έ"�����D���/[�k�9���Y�8ds|\���Ҿp6�Ҵ���]��.����6�z<�v��@]�i%��$j��~�g��J>��no����pM[me�i$[����s�o�ᘨ�˸ nɜG-�ĨU�ycP�3.DB�li�;��hj���x7Z^�N�h������N3u{�:j�x�힞��#M&��jL P@_���� P��&��o8������9�����@Sz6�t7#O�ߋ �s}Yf�T���lmr����Z)'N��k�۞p����w\�Tȯ?�8`�O��i{wﭹW�[�r�� ��Q4F�׊���3m&L�=��h3����z~��#�\�l :�F,j@�� ʱ�wQT����8�"kJO���6�֚l����}���R�>ډK���]��y����&����p�}b��;N�1�m�r$�|��7�>e�@B�TM*-iH��g�D�)� E�m�|�ؘbҗ�a��Ҿ����t4���o���G��*oCN�rP���Q��@z,|?W[0�����:�n,jWiE��W��$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S����VN;�}�s?.����� w�9��˟<���Mq4�Wv'��{)0�1mB��V����W[�����8�/<� �%���wT^�5���b��)iM� pg�N�&ݝ��VO~�q���u���9� ����!��J27����$O-���! �:�%H��� ـ����y�ΠM=t{!S�� oK8������t<����è:a������[�����ա�H���~��w��Qz`�po�^ ����Q��n� �,uu�C�$ ^���,������8�#��:�6��e�|~���!�3�3.�\0��q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�<���Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y�|�y��� ~�6�@c��1vOp�Ig����4��l�OD���L����� R���c���j�_�uX6��3?nk��Wy�f;^*B� ��@�~a�`��Eu������+���6�L��.ü>��}y���}_�O�6�͐�:�YrG�X��kG�����l^w���~㒶sy��Iu�!� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT��G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2��#I/�׍qz��^t�̔���b�Yz4x���t�){ OH��+(E��A&�N�������XT��o��"�XC��'���)}�J�z�p� ��~5�}�^����+�6����w��c��Q�|Lp�d�H��}�(�.|����k��c4^�"�����Z?ȕ ��a<�L�!039C� �Eu�C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf���M}7����]����s2tcS{�\icTx;�\��7K���P���ʇ Z O-��~��c>"��?�������P��E��O�8��@�8��G��Q�g�a�Վ���󁶠�䧘��_%#r�>�1�z�a��eb��qcPѵ��n���#L��� =��׀t� L�7�`��V���A{�C:�g���e@�w1 Xp3�c3�ġ����p��M"'-�@n4���fG��B3�DJ�8[Jo�ߐ���gK)ƛ��$���� ���8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`���?��K����QK�d����B`�s}�>���`��*�>��,*@J�d�oF*����弝��O}�k��s��]��y�ߘ��c1G�V���<=�7��7����6�q�PT��tXԀ�!9*4�4Tހ3XΛex�46���Y��D ����� �BdemDa����\�_l,��G�/���֌7���Y�](�xTt^%�GE�����4�}bT���ڹ�����;Y)���B�Q��u��>J/J �⮶.�XԄ��j�ݳ�+E��d ��r�5�_D�1 ��o�� �B�x�΢�#���<��W�����8���R6�@g�M�.��� dr�D��>(otU��@x=��~v���2� ӣ�d�oBd��3�eO�6�㣷�����ݜ6��6Y��Qz`��S��{���\P�~z m5{J/L��1������<�e�ͅPu�b�]�ϔ���'������f�b� Zpw��c`"��i���BD@:)ִ�:�]��hv�E�w���T�l��P���"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u�)]կ�����7x���s�M�-<ɯ�c��r�v�����@��$�ޮ}lk���a���'����>x��O\�ZFu>�����ck#��&:��`�$�ai�>2Δ����l���oF[h��lE�ܺ�Πk:)���`�� $[6�����9�����kOw�\|���8}������ބ:��񶐕��I�A1/�=�2[�,�!��.}gN#�u����b��� ~��݊��}34q����d�E��Lc��$��"�[q�U�硬g^��%B �z���r�pJ�ru%v\h1Y�ne`ǥ:g���pQM~�^�Xi� ��`S�:V29.�P���V�?B�k�� AEvw%�_�9C�Q����wKekPؠ�\�;Io d�{ ߞo�c1eP����\� `����E=���@K<�Y���eڼ�J���w����{av�F�'�M�@/J��+9p���|]�����Iw &`��8���&M�hg��[�{��Xj��%��Ӓ�$��(����ʹN���<>�I���RY���K2�NPlL�ɀ)��&e����B+ь����( � �JTx���_?EZ� }@ 6�U���뙢ط�z��dWI�n` D����噥�[��uV��"�G&Ú����2g�}&m��?ċ�"����Om#��������� ��{�ON��"S�X��Ne��ysQ���@Fn��Vg���dX�~nj�]J�<�K]:��FW��b�������62�=��5f����JKw��bf�X�55��~J �%^����:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v���g�8�1��f24;�V���ǔ�)����9���1\��c��v�/'Ƞ�w�������$�4�R-��t���� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃������A��20�c#��@ 0!1@AP"#2Q`$3V�%45a6�FRUq��� ����^7ׅ,$n�������+��F�`��2X'��0vM��p�L=������5��8������u�p~���.�`r�����\���O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;������S�T���1���i[U�ɵz�]��U)V�S6���3$K{�ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u�!��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� ���D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v�����멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$�JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4�[�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ�dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il��d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J��oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.���3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R=X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N�����#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%���JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$���L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����)�H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>�dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\�y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~������Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7��ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz��)�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!)'��8Ϣ�ٔ��ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'�L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���(}��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3�������QT��a����x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3�,#c�co��q�a)*Pt����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O���������m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l�O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m���PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q������~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~�|,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)`7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ����}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i����>J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0��Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q��nxҍ!U�f�!eh�i�2�m���`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{/ �?�͟��|1�:�#g��W�>$����d��J��d�B��=��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H����O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l����l�cGs�ځ�������y�Ac�����\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6����N6�q������N ! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td�� ?���N����a��3��m���C���w��������xA�m�q�m���m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4���r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0������u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ����V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G�����Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q�(v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9��3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M����A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#yw��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa����Q�#� WeF��ŮNj�p�J* mQ�N����*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]��陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv:��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WPw���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp�BGv[]�u�Ov���0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND��.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J �4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R��� ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`(�>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG��{+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/��*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�>e]�����Q�r�:����g�,i"�����ԩA�*M�<�G��b�if��l^M��5� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f�����֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ���˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)��h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~������ G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W�I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��BS�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c���]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪�\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W�����6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}�����6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@�P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6�����m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw�k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ����mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e��R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1[y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN��ĕ��0 !01@Q"2AaPq3BR������?���@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j�ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^���ԯ̾9Z��F��������n��1��� ��]�[��)�'������:�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ��&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0��;_��3 !01"@AQa2Pq#3BR������?��ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6����������4B>��o��](��$B���m�����a�!=��?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)����I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a�84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b��$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7��k�� 403WebShell
403Webshell
Server IP : 199.79.62.87  /  Your IP : 216.73.216.188
Web Server : Microsoft-IIS/10.0
System : Windows NT BH-PLESK-WEB4 10.0 build 20348 (Windows Server 2022) AMD64
User : IWPD_13108(aitsti.c) ( 0)
PHP Version : 8.3.14
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  D:/INETPUB/VHOSTS/aitsti.com/httpdocs/vendor/mpdf/mpdf/classes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : D:/INETPUB/VHOSTS/aitsti.com/httpdocs/vendor/mpdf/mpdf/classes/indic.php
<?php

require_once __DIR__ . '/../MpdfException.php';

class INDIC
{
	/* FROM hb-ot-shape-complex-indic-private.hh */

	// indic_category
	const OT_X = 0;
	const OT_C = 1;
	const OT_V = 2;
	const OT_N = 3;
	const OT_H = 4;
	const OT_ZWNJ = 5;
	const OT_ZWJ = 6;
	const OT_M = 7; /* Matra or Dependent Vowel */
	const OT_SM = 8;
	const OT_VD = 9;
	const OT_A = 10;
	const OT_NBSP = 11;
	const OT_DOTTEDCIRCLE = 12; /* Not in the spec, but special in Uniscribe. /Very very/ special! */
	const OT_RS = 13; /* Register Shifter, used in Khmer OT spec */
	const OT_Coeng = 14;
	const OT_Repha = 15;

	const OT_Ra = 16; /* Not explicitly listed in the OT spec, but used in the grammar. */
	const OT_CM = 17;

	// Based on indic_category used to make string to find syllables
	// OT_ to string character (using e.g. OT_C from INDIC) hb-ot-shape-complex-indic-private.hh
	public static $indic_category_char = array(
		'x',
		'C',
		'V',
		'N',
		'H',
		'Z',
		'J',
		'M',
		'S',
		'v',
		'A', /* Spec gives Andutta U+0952 as OT_A. However, testing shows that Uniscribe
		 * treats U+0951..U+0952 all as OT_VD - see set_indic_properties */
		's',
		'D',
		'F', /* Register shift Khmer only */
		'G', /* Khmer only */
		'r', /* 0D4E (dot reph) only one in Malayalam */
		'R',
		'm', /* Consonant medial only used in Indic 0A75 in Gurmukhi  (0A00..0A7F)  : also in Lao, Myanmar, Tai Tham, Javanese & Cham  */
	);

	/* Visual positions in a syllable from left to right. */
	/* FROM hb-ot-shape-complex-indic-private.hh */

	// indic_position
	const POS_START = 0;

	const POS_RA_TO_BECOME_REPH = 1;
	const POS_PRE_M = 2;
	const POS_PRE_C = 3;

	const POS_BASE_C = 4;
	const POS_AFTER_MAIN = 5;

	const POS_ABOVE_C = 6;

	const POS_BEFORE_SUB = 7;
	const POS_BELOW_C = 8;
	const POS_AFTER_SUB = 9;

	const POS_BEFORE_POST = 10;
	const POS_POST_C = 11;
	const POS_AFTER_POST = 12;

	const POS_FINAL_C = 13;
	const POS_SMVD = 14;

	const POS_END = 15;

	/*
	 * Basic features.
	 * These features are applied in order, one at a time, after initial_reordering.
	 */
	/*
	 * Must be in the same order as the indic_features array. Ones starting with _ are F_GLOBAL
	 * Ones without the _ are only applied where the mask says!
	 */

	const _NUKT = 0;
	const _AKHN = 1;
	const RPHF = 2;
	const _RKRF = 3;
	const PREF = 4;
	const BLWF = 5;
	const HALF = 6;
	const ABVF = 7;
	const PSTF = 8;
	const CFAR = 9; // Khmer only
	const _VATU = 10;
	const _CJCT = 11;
	const INIT = 12;

	public static function set_indic_properties(&$info, $scriptblock)
	{
		$u = $info['uni'];
		$type = self::indic_get_categories($u);
		$cat = ($type & 0x7F);
		$pos = ($type >> 8);

		/*
		 * Re-assign category
		 */

		if ($u == 0x17D1)
			$cat = self::OT_X;

		if ($cat == self::OT_X && self::in_range($u, 0x17CB, 0x17D3)) { /* Khmer Various signs */
			/* These are like Top Matras. */
			$cat = self::OT_M;
			$pos = self::POS_ABOVE_C;
		}

		if ($u == 0x17C6)
			$cat = self::OT_N; /* Khmer Bindu doesn't like to be repositioned. */

		if ($u == 0x17D2)
			$cat = self::OT_Coeng; /* Khmer coeng */

		/* The spec says U+0952 is OT_A.	However, testing shows that Uniscribe
		 * treats U+0951..U+0952 all as OT_VD.
		 * TESTS:
		 * U+092E,U+0947,U+0952
		 * U+092E,U+0952,U+0947
		 * U+092E,U+0947,U+0951
		 * U+092E,U+0951,U+0947
		 * */
		//if ($u == 0x0952) $cat = self::OT_A;
		if (self::in_range($u, 0x0951, 0x0954))
			$cat = self::OT_VD;

		if ($u == 0x200C)
			$cat = self::OT_ZWNJ;
		else if ($u == 0x200D)
			$cat = self::OT_ZWJ;
		else if ($u == 0x25CC)
			$cat = self::OT_DOTTEDCIRCLE;
		else if ($u == 0x0A71)
			$cat = self::OT_SM; /* GURMUKHI ADDAK.	More like consonant medial. like 0A75. */

		if ($cat == self::OT_Repha) {
			/* There are two kinds of characters marked as Repha:
			 * - The ones that are GenCat=Mn are already positioned visually, ie. after base. (eg. Khmer)
			 * - The ones that are GenCat=Lo is encoded logically, ie. beginning of syllable. (eg. Malayalam)
			 *
			 * We recategorize the first kind to look like a Nukta and attached to the base directly.
			 */
			if ($info['general_category'] == UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
				$cat = self::OT_N;
		}

		/*
		 * Re-assign position.
		 */

		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
			if ($scriptblock == UCDN::SCRIPT_KHMER)
				$pos = self::POS_BELOW_C; /* Khmer differs from Indic here. */
			else
				$pos = self::POS_BASE_C; /* Will recategorize later based on font lookups. */

			if (self::is_ra($u))
				$cat = self::OT_Ra;
		}
		else if ($cat == self::OT_M) {
			$pos = self::matra_position($u, $pos);
		} else if ($cat == self::OT_SM || $cat == self::OT_VD) {
			$pos = self::POS_SMVD;
		}

		if ($u == 0x0B01)
			$pos = self::POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */

		$info['indic_category'] = $cat;
		$info['indic_position'] = $pos;
	}

	// syllable_type
	const CONSONANT_SYLLABLE = 0;
	const VOWEL_SYLLABLE = 1;
	const STANDALONE_CLUSTER = 2;
	const BROKEN_CLUSTER = 3;
	const NON_INDIC_CLUSTER = 4;

	public static function set_syllables(&$o, $s, &$broken_syllables)
	{
		$ptr = 0;
		$syllable_serial = 1;
		$broken_syllables = false;

		while ($ptr < strlen($s)) {
			$match = '';
			$syllable_length = 1;
			$syllable_type = self::NON_INDIC_CLUSTER;
			// CONSONANT_SYLLABLE Consonant syllable
			// From OT spec:
			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)) {
				// From HarfBuzz:
				//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)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::CONSONANT_SYLLABLE;
			}
			// VOWEL_SYLLABLE Vowel-based syllable
			// From OT spec:
			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)) {
				// From HarfBuzz:
				//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)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::VOWEL_SYLLABLE;
			}

			/* Apply only if it's a word start. */
			// STANDALONE_CLUSTER Stand Alone syllable at start of word
			// From OT spec:
			else if (($ptr == 0 ||
				$o[$ptr - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER ||
				$o[$ptr - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK
				) && (preg_match('/^(RH|r)?[sD][N]?([ZJ]?H[CR]m*)?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s, $ptr), $ma))) {
				// From HarfBuzz:
				// && (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)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::STANDALONE_CLUSTER;
			}

			// BROKEN_CLUSTER syllable
			else if (preg_match('/^(RH|r)?[N]?([ZJ]?H[CR])?([M]*[N]?[H]?)?[S]?[v]{0,2}/', substr($s, $ptr), $ma)) {
				// From HarfBuzz:
				//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)) {
				if (strlen($ma[0])) { // May match blank
					$syllable_length = strlen($ma[0]);
					$syllable_type = self::BROKEN_CLUSTER;
					$broken_syllables = true;
				}
			}

			for ($i = $ptr; $i < $ptr + $syllable_length; $i++) {
				$o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type;
			}
			$ptr += $syllable_length;
			$syllable_serial++;
			if ($syllable_serial == 16)
				$syllable_serial = 1;
		}
	}

	public static function set_syllables_sinhala(&$o, $s, &$broken_syllables)
	{
		$ptr = 0;
		$syllable_serial = 1;
		$broken_syllables = false;

		while ($ptr < strlen($s)) {
			$match = '';
			$syllable_length = 1;
			$syllable_type = self::NON_INDIC_CLUSTER;
			// CONSONANT_SYLLABLE Consonant syllable
			// From OT spec:
			if (preg_match('/^([CR]HJ|[CR]JH){0,8}[CR][HM]{0,3}[S]{0,1}/', substr($s, $ptr), $ma)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::CONSONANT_SYLLABLE;
			}
			// VOWEL_SYLLABLE Vowel-based syllable
			// From OT spec:
			else if (preg_match('/^V[S]{0,1}/', substr($s, $ptr), $ma)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::VOWEL_SYLLABLE;
			}

			for ($i = $ptr; $i < $ptr + $syllable_length; $i++) {
				$o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type;
			}
			$ptr += $syllable_length;
			$syllable_serial++;
			if ($syllable_serial == 16)
				$syllable_serial = 1;
		}
	}

	public static function set_syllables_khmer(&$o, $s, &$broken_syllables)
	{
		$ptr = 0;
		$syllable_serial = 1;
		$broken_syllables = false;

		while ($ptr < strlen($s)) {
			$match = '';
			$syllable_length = 1;
			$syllable_type = self::NON_INDIC_CLUSTER;
			// CONSONANT_SYLLABLE Consonant syllable
			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)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::CONSONANT_SYLLABLE;
			}
			// VOWEL_SYLLABLE Vowel-based syllable
			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)) {
				$syllable_length = strlen($ma[0]);
				$syllable_type = self::VOWEL_SYLLABLE;
			}


			// BROKEN_CLUSTER syllable
			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)) {
				if (strlen($ma[0])) { // May match blank
					$syllable_length = strlen($ma[0]);
					$syllable_type = self::BROKEN_CLUSTER;
					$broken_syllables = true;
				}
			}

			for ($i = $ptr; $i < $ptr + $syllable_length; $i++) {
				$o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type;
			}
			$ptr += $syllable_length;
			$syllable_serial++;
			if ($syllable_serial == 16)
				$syllable_serial = 1;
		}
	}

	public static function initial_reordering(&$info, $GSUBdata, $broken_syllables, $indic_config, $scriptblock, $is_old_spec, $dottedcircle)
	{

		self::update_consonant_positions($info, $GSUBdata);

		if ($broken_syllables && $dottedcircle) {
			self::insert_dotted_circles($info, $dottedcircle);
		}

		$count = count($info);
		if (!$count)
			return;
		$last = 0;
		$last_syllable = $info[0]['syllable'];
		for ($i = 1; $i < $count; $i++) {
			if ($last_syllable != $info[$i]['syllable']) {
				self::initial_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
				$last = $i;
				$last_syllable = $info[$last]['syllable'];
			}
		}
		self::initial_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
	}

	public static function update_consonant_positions(&$info, $GSUBdata)
	{
		$count = count($info);
		for ($i = 0; $i < $count; $i++) {
			if ($info[$i]['indic_position'] == self::POS_BASE_C) {
				$c = $info[$i]['uni'];
				// If would substitute...
				if (isset($GSUBdata['pref'][$c])) {
					$info[$i]['indic_position'] = self::POS_POST_C;
				} else if (isset($GSUBdata['blwf'][$c])) {
					$info[$i]['indic_position'] = self::POS_BELOW_C;
				} else if (isset($GSUBdata['pstf'][$c])) {
					$info[$i]['indic_position'] = self::POS_POST_C;
				}
			}
		}
	}

	public static function insert_dotted_circles(&$info, $dottedcircle)
	{
		$idx = 0;
		$last_syllable = 0;
		while ($idx < count($info)) {
			$syllable = $info[$idx]['syllable'];
			$syllable_type = ($syllable & 0x0F);
			if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
				$last_syllable = $syllable;

				$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];

				/* Insert dottedcircle after possible Repha. */
				while ($idx < count($info) && $last_syllable == $info[$idx]['syllable'] && $info[$idx]['indic_category'] == self::OT_Repha)
					$idx++;
				array_splice($info, $idx, 0, $dottedcircle);
			} else {
				$idx++;
			}
		}

		// I am not sue how this code below got in here, since $idx should now be > count($info) and thus invalid.
		// In case I am missing something(!) I'll leave a warning here for now:
		if (isset($info[$idx])) {
			throw new MpdfException('Unexpected error occured in Indic processing');
		}
		// In case of final bloken cluster...
		//$syllable = $info[$idx]['syllable'];
		//$syllable_type = ($syllable & 0x0F);
		//if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
		//	$dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
		//	array_splice($info, $idx, 0, $dottedcircle);
		//}
	}

	/* Rules from:
	 * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */

	public static function initial_reordering_syllable(&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end)
	{
		/* vowel_syllable: We made the vowels look like consonants. So uses the consonant logic! */
		/* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */
		/* standalone_cluster: We treat NBSP/dotted-circle as if they are consonants, so we should just chain. */

		$syllable_type = ($info[$start]['syllable'] & 0x0F);
		if ($syllable_type == self::NON_INDIC_CLUSTER) {
			return;
		}
		if ($syllable_type == self::BROKEN_CLUSTER || $syllable_type == self::STANDALONE_CLUSTER) {
			//if ($uniscribe_bug_compatible) {
			/* For dotted-circle, this is what Uniscribe does:
			 * If dotted-circle is the last glyph, it just does nothing.
			 * i.e. It doesn't form Reph. */
			if ($info[$end - 1]['indic_category'] == self::OT_DOTTEDCIRCLE) {
				return;
			}
		}

		/* 1. Find base consonant:
		 *
		 * The shaping engine finds the base consonant of the syllable, using the
		 * following algorithm: starting from the end of the syllable, move backwards
		 * until a consonant is found that does not have a below-base or post-base
		 * form (post-base forms have to follow below-base forms), or that is not a
		 * pre-base reordering Ra, or arrive at the first consonant. The consonant
		 * stopped at will be the base.
		 *
		 * 	o If the syllable starts with Ra + Halant (in a script that has Reph)
		 * 	and has more than one consonant, Ra is excluded from candidates for
		 * 	base consonants.
		 */

		$base = $end;
		$has_reph = false;
		$limit = $start;

		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			/* -> If the syllable starts with Ra + Halant (in a script that has Reph)
			 * 	and has more than one consonant, Ra is excluded from candidates for
			 * 	base consonants. */
			if (count($GSUBdata['rphf']) /* ?? $indic_plan->mask_array[RPHF] */ && $start + 3 <= $end &&
				(
				($indic_config[4] == self::REPH_MODE_IMPLICIT && !self::is_joiner($info[$start + 2])) ||
				($indic_config[4] == self::REPH_MODE_EXPLICIT && $info[$start + 2]['indic_category'] == self::OT_ZWJ)
				)) {
				/* See if it matches the 'rphf' feature. */
				//$glyphs = array($info[$start]['uni'], $info[$start + 1]['uni']);
				//if ($indic_plan->rphf->would_substitute ($glyphs, count($glyphs), true, face)) {
				if (isset($GSUBdata['rphf'][$info[$start]['uni']]) && self::is_halant_or_coeng($info[$start + 1])) {
					$limit += 2;
					while ($limit < $end && self::is_joiner($info[$limit]))
						$limit++;
					$base = $start;
					$has_reph = true;
				}
			} else if ($indic_config[4] == self::REPH_MODE_LOG_REPHA && $info[$start]['indic_category'] == self::OT_Repha) {
				$limit += 1;
				while ($limit < $end && self::is_joiner($info[$limit]))
					$limit++;
				$base = $start;
				$has_reph = true;
			}
		}

		switch ($indic_config[2]) { // base_pos
			case self::BASE_POS_LAST:
				/* -> starting from the end of the syllable, move backwards */
				$i = $end;
				$seen_below = false;
				do {
					$i--;
					/* -> until a consonant is found */
					if (self::is_consonant($info[$i])) {
						/* -> that does not have a below-base or post-base form
						 * (post-base forms have to follow below-base forms), */
						if ($info[$i]['indic_position'] != self::POS_BELOW_C && ($info[$i]['indic_position'] != self::POS_POST_C || $seen_below)) {
							$base = $i;
							break;
						}
						if ($info[$i]['indic_position'] == self::POS_BELOW_C)
							$seen_below = true;

						/* -> or that is not a pre-base reordering Ra,
						 *
						 * IMPLEMENTATION NOTES:
						 *
						 * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
						 * by the logic above already.
						 */

						/* -> or arrive at the first consonant. The consonant stopped at will
						 * be the base. */
						$base = $i;
					}
					else {
						/* A ZWJ after a Halant stops the base search, and requests an explicit
						 * half form.
						 * [A ZWJ before a Halant, requests a subjoined form instead, and hence
						 * search continues. This is particularly important for Bengali
						 * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya] */
						if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWJ && $info[$i - 1]['indic_category'] == self::OT_H) {
							if (!defined("OMIT_INDIC_FIX_1") || OMIT_INDIC_FIX_1 != 1) {
								$base = $i;
							} // INDIC_FIX_1
							break;
						}
						// ZKI8
						if ($start < $i && $info[$i]['indic_category'] == self::OT_ZWNJ) {
							break;
						}
					}
				} while ($i > $limit);
				break;

			case self::BASE_POS_FIRST:
				/* In scripts without half forms (eg. Khmer), the first consonant is always the base. */

				if (!$has_reph)
					$base = $limit;

				/* Find the last base consonant that is not blocked by ZWJ.	If there is
				 * a ZWJ right before a base consonant, that would request a subjoined form. */
				for ($i = $limit; $i < $end; $i++) {
					if (self::is_consonant($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C) {
						if ($limit < $i && $info[$i - 1]['indic_category'] == self::OT_ZWJ)
							break;
						else
							$base = $i;
					}
				}

				/* Mark all subsequent consonants as below. */
				for ($i = $base + 1; $i < $end; $i++) {
					if (self::is_consonant($info[$i]) && $info[$i]['indic_position'] == self::POS_BASE_C)
						$info[$i]['indic_position'] = self::POS_BELOW_C;
				}
				break;
			//default:
			//assert (false);
			/* fallthrough */
		}

		/* -> If the syllable starts with Ra + Halant (in a script that has Reph)
		 * 	and has more than one consonant, Ra is excluded from candidates for
		 * 	base consonants.
		 *
		 * 	Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			if ($has_reph && $base == $start && $limit - $base <= 2) {
				/* Have no other consonant, so Reph is not formed and Ra becomes base. */
				$has_reph = false;
			}
		}

		/* 2. Decompose and reorder Matras:
		 *
		 * Each matra and any syllable modifier sign in the cluster are moved to the
		 * appropriate position relative to the consonant(s) in the cluster. The
		 * shaping engine decomposes two- or three-part matras into their constituent
		 * parts before any repositioning. Matra characters are classified by which
		 * consonant in a conjunct they have affinity for and are reordered to the
		 * following positions:
		 *
		 * 		o Before first half form in the syllable
		 * 		o After subjoined consonants
		 * 		o After post-form consonant
		 * 		o After main consonant (for above marks)
		 *
		 * IMPLEMENTATION NOTES:
		 *
		 * The normalize() routine has already decomposed matras for us, so we don't
		 * need to worry about that.
		 */


		/* 3.	Reorder marks to canonical order:
		 *
		 * Adjacent nukta and halant or nukta and vedic sign are always repositioned
		 * if necessary, so that the nukta is first.
		 *
		 * IMPLEMENTATION NOTES:
		 *
		 * Use the combining Class from Unicode categories? to bubble_sort.
		 */

		/* Reorder characters */

		for ($i = $start; $i < $base; $i++)
			$info[$i]['indic_position'] = min(self::POS_PRE_C, $info[$i]['indic_position']);

		if ($base < $end)
			$info[$base]['indic_position'] = self::POS_BASE_C;

		/* Mark final consonants. A final consonant is one appearing after a matra,
		 * ? only in Khmer. */
		for ($i = $base + 1; $i < $end; $i++)
			if ($info[$i]['indic_category'] == self::OT_M) {
				for ($j = $i + 1; $j < $end; $j++)
					if (self::is_consonant($info[$j])) {
						$info[$j]['indic_position'] = self::POS_FINAL_C;
						break;
					}
				break;
			}

		/* Handle beginning Ra */
		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			if ($has_reph)
				$info[$start]['indic_position'] = self::POS_RA_TO_BECOME_REPH;
		}


		/* For old-style Indic script tags, move the first post-base Halant after
		 * last consonant.	Only do this if there is *not* a Halant after last
		 * consonant. Otherwise it becomes messy. */
		if ($is_old_spec) {
			for ($i = $base + 1; $i < $end; $i++) {
				if ($info[$i]['indic_category'] == self::OT_H) {
					for ($j = $end - 1; $j > $i; $j--) {
						if (self::is_consonant($info[$j]) || $info[$j]['indic_category'] == self::OT_H) {
							break;
						}
					}
					if ($info[$j]['indic_category'] != self::OT_H && $j > $i) {
						/* Move Halant to after last consonant. */
						self::_move_info_pos($info, $i, $j + 1);
					}
					break;
				}
			}
		}

		/* Attach misc marks to previous char to move with them. */
		$last_pos = self::POS_START;
		for ($i = $start; $i < $end; $i++) {
			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) ))) {
				$info[$i]['indic_position'] = $last_pos;
				if ($info[$i]['indic_category'] == self::OT_H && $info[$i]['indic_position'] == self::POS_PRE_M) {
					/*
					 * Uniscribe doesn't move the Halant with Left Matra.
					 * TEST: U+092B,U+093F,U+094DE
					 * We follow.	This is important for the Sinhala
					 * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
					 * where U+0DD9 is a left matra and U+0DCA is the virama.
					 * We don't want to move the virama with the left matra.
					 * TEST: U+0D9A,U+0DDA
					 */
					for ($j = $i; $j > $start; $j--)
						if ($info[$j - 1]['indic_position'] != self::POS_PRE_M) {
							$info[$i]['indic_position'] = $info[$j - 1]['indic_position'];
							break;
						}
				}
			} else if ($info[$i]['indic_position'] != self::POS_SMVD) {
				$last_pos = $info[$i]['indic_position'];
			}
		}

		/* Re-attach ZWJ, ZWNJ, and halant to next char, for after-base consonants. */
		$last_halant = $end;
		for ($i = $base + 1; $i < $end; $i++) {
			if (self::is_halant_or_coeng($info[$i]))
				$last_halant = $i;
			else if (self::is_consonant($info[$i])) {
				for ($j = $last_halant; $j < $i; $j++)
					if ($info[$j]['indic_position'] != self::POS_SMVD)
						$info[$j]['indic_position'] = $info[$i]['indic_position'];
			}
		}


		if ($scriptblock == UCDN::SCRIPT_KHMER) {
			/* KHMER_FIX_2 */
			/* Move Coeng+RO (Halant,Ra) sequence before base consonant. */
			for ($i = $base + 1; $i < $end; $i++) {
				if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni'])) {
					$info[$i]['indic_position'] = self::POS_PRE_C;
					$info[$i + 1]['indic_position'] = self::POS_PRE_C;
					break;
				}
			}
		}


		/*
		  if (!defined("OMIT_INDIC_FIX_2") || OMIT_INDIC_FIX_2 != 1) {
		  // INDIC_FIX_2
		  $ZWNJ_found = false;
		  $POST_ZWNJ_c_found = false;
		  for ($i = $base + 1; $i < $end; $i++) {
		  if ($info[$i]['indic_category'] == self::OT_ZWNJ) { $ZWNJ_found = true; }
		  else if ($ZWNJ_found && $info[$i]['indic_category'] == self::OT_C) { $POST_ZWNJ_c_found = true; }
		  else if ($POST_ZWNJ_c_found && $info[$i]['indic_position'] == self::POS_BEFORE_SUB) { $info[$i]['indic_position'] = self::POS_AFTER_SUB; }
		  }
		  }
		 */

		/* Setup masks now */
		for ($i = $start; $i < $end; $i++) {
			$info[$i]['mask'] = 0;
		}


		if ($scriptblock == UCDN::SCRIPT_KHMER) {
			/* Find a Coeng+RO (Halant,Ra) sequence and mark it for pre-base processing. */
			$mask = self::FLAG(self::PREF);
			for ($i = $base; $i < $end - 1; $i++) { /* KHMER_FIX_1 From $start (not base) */
				if (self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni'])) {

					$info[$i]['mask'] |= self::FLAG(self::PREF);
					$info[$i + 1]['mask'] |= self::FLAG(self::PREF);

					/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
					 * Read the feature spec.
					 * This allows distinguishing the following cases with MS Khmer fonts:
					 * U+1784,U+17D2,U+179A,U+17D2,U+1782  [C+Coeng+RO+Coeng+C] => Should activate CFAR
					 * U+1784,U+17D2,U+1782,U+17D2,U+179A  [C+Coeng+C+Coeng+RO] => Should NOT activate CFAR
					 */
					for ($j = ($i + 2); $j < $end; $j++)
						$info[$j]['mask'] |= self::FLAG(self::CFAR);

					break;
				}
			}
		}



		/* Sit tight, rock 'n roll! */
		self::bubble_sort($info, $start, $end - $start);

		/* Find base again */
		$base = $end;
		for ($i = $start; $i < $end; $i++) {
			if ($info[$i]['indic_position'] == self::POS_BASE_C) {
				$base = $i;
				break;
			}
		}

		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			/* Reph */
			for ($i = $start; $i < $end; $i++) {
				if ($info[$i]['indic_position'] == self::POS_RA_TO_BECOME_REPH) {
					$info[$i]['mask'] |= self::FLAG(self::RPHF);
				}
			}

			/* Pre-base */
			$mask = self::FLAG(self::HALF);
			for ($i = $start; $i < $base; $i++) {
				$info[$i]['mask'] |= $mask;
			}
		}

		/* Post-base */
		$mask = (self::FLAG(self::BLWF) | self::FLAG(self::ABVF) | self::FLAG(self::PSTF));
		for ($i = $base + 1; $i < $end; $i++) {
			$info[$i]['mask'] |= $mask;
		}


		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			if (!defined("OMIT_INDIC_FIX_3") || OMIT_INDIC_FIX_3 != 1) {
				/* INDIC_FIX_3 */
				/* Find a (pre-base) Consonant, Halant,Ra sequence and mark Halant|Ra for below-base BLWF processing. */
				// TEST CASE &#x995;&#x9cd;&#x9b0;&#x9cd;&#x995; in FreeSans versus Vrinda
				if (($base - $start) >= 3) {
					for ($i = $start; $i < ($base - 2); $i++) {
						if (self::is_consonant($info[$i])) {
							if (self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i + 2]['uni'])) {
								// If would substitute Halant+Ra...BLWF
								if (isset($GSUBdata['blwf'][$info[$i + 2]['uni']])) {
									$info[$i + 1]['mask'] |= self::FLAG(self::BLWF);
									$info[$i + 2]['mask'] |= self::FLAG(self::BLWF);
								}
								/* If would not substitute as blwf, mark Ra+Halant for RPHF using following Halant (if present) */ else if (self::is_halant_or_coeng($info[$i + 3])) {
									$info[$i + 2]['mask'] |= self::FLAG(self::RPHF);
									$info[$i + 3]['mask'] |= self::FLAG(self::RPHF);
								}
								break;
							}
						}
					}
				}
			}
		}



		if ($is_old_spec && $scriptblock == UCDN::SCRIPT_DEVANAGARI) {
			/* Old-spec eye-lash Ra needs special handling.	From the spec:
			 * "The feature 'below-base form' is applied to consonants
			 * having below-base forms and following the base consonant.
			 * The exception is vattu, which may appear below half forms
			 * as well as below the base glyph. The feature 'below-base
			 * form' will be applied to all such occurrences of Ra as well."
			 *
			 * Test case: U+0924,U+094D,U+0930,U+094d,U+0915
			 * with Sanskrit 2003 font.
			 *
			 * However, note that Ra,Halant,ZWJ is the correct way to
			 * request eyelash form of Ra, so we wouldbn't inhibit it
			 * in that sequence.
			 *
			 * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
			 */
			for ($i = $start; ($i + 1) < $base; $i++) {
				if ($info[$i]['indic_category'] == self::OT_Ra && $info[$i + 1]['indic_category'] == self::OT_H &&
					($i + 2 == $base || $info[$i + 2]['indic_category'] != self::OT_ZWJ)) {
					$info[$i]['mask'] |= self::FLAG(self::BLWF);
					$info[$i + 1]['mask'] |= self::FLAG(self::BLWF);
				}
			}
		}

		if ($scriptblock != UCDN::SCRIPT_KHMER) {
			if (count($GSUBdata['pref']) && $base + 2 < $end) {
				/* Find a Halant,Ra sequence and mark it for pre-base processing. */
				for ($i = $base + 1; $i + 1 < $end; $i++) {
					// If old_spec find Ra-Halant...
					if ((isset($GSUBdata['pref'][$info[$i + 1]['uni']]) && self::is_halant_or_coeng($info[$i]) && self::is_ra($info[$i + 1]['uni']) ) ||
						($is_old_spec && isset($GSUBdata['pref'][$info[$i]['uni']]) && self::is_halant_or_coeng($info[$i + 1]) && self::is_ra($info[$i]['uni']) )
					) {
						$info[$i++]['mask'] |= self::FLAG(self::PREF);
						$info[$i++]['mask'] |= self::FLAG(self::PREF);
						break;
					}
				}
			}
		}


		/* Apply ZWJ/ZWNJ effects */
		for ($i = $start + 1; $i < $end; $i++) {
			if (self::is_joiner($info[$i])) {
				$non_joiner = ($info[$i]['indic_category'] == self::OT_ZWNJ);
				$j = $i;
				while ($j > $start) {
					if (defined("OMIT_INDIC_FIX_4") && OMIT_INDIC_FIX_4 == 1) {
						// INDIC_FIX_4 = do nothing - carry on //
						// ZWNJ should block H C from forming blwf post-base - need to unmask backwards beyond first consonant arrived at //
						if (!self::is_consonant($info[$j])) {
							break;
						}
					}
					$j--;

					/* ZWJ/ZWNJ should disable CJCT.	They do that by simply
					 * being there, since we don't skip them for the CJCT
					 * feature (ie. F_MANUAL_ZWJ) */

					/* A ZWNJ disables HALF. */
					if ($non_joiner) {
						$info[$j]['mask'] &= ~(self::FLAG(self::HALF) | self::FLAG(self::BLWF));
					}
				}
			}
		}
	}

	public static function final_reordering(&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec)
	{
		$count = count($info);
		if (!$count)
			return;
		$last = 0;
		$last_syllable = $info[0]['syllable'];
		for ($i = 1; $i < $count; $i++) {
			if ($last_syllable != $info[$i]['syllable']) {
				self::final_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $i);
				$last = $i;
				$last_syllable = $info[$last]['syllable'];
			}
		}
		self::final_reordering_syllable($info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $last, $count);
	}

	public static function final_reordering_syllable(&$info, $GSUBdata, $indic_config, $scriptblock, $is_old_spec, $start, $end)
	{

		/* 4. Final reordering:
		 *
		 * After the localized forms and basic shaping forms GSUB features have been
		 * applied (see below), the shaping engine performs some final glyph
		 * reordering before applying all the remaining font features to the entire
		 * cluster.
		 */

		/* Find base again */
		for ($base = $start; $base < $end; $base++)
			if ($info[$base]['indic_position'] >= self::POS_BASE_C) {
				if ($start < $base && $info[$base]['indic_position'] > self::POS_BASE_C)
					$base--;
				break;
			}
		if ($base == $end && $start < $base && $info[$base - 1]['indic_category'] != self::OT_ZWJ)
			$base--;
		while ($start < $base && isset($info[$base]) && ($info[$base]['indic_category'] == self::OT_H || $info[$base]['indic_category'] == self::OT_N))
			$base--;


		/* 	o Reorder matras:
		 *
		 * 	If a pre-base matra character had been reordered before applying basic
		 * 	features, the glyph can be moved closer to the main consonant based on
		 * 	whether half-forms had been formed. Actual position for the matra is
		 * 	defined as "after last standalone halant glyph, after initial matra
		 * 	position and before the main consonant". If ZWJ or ZWNJ follow this
		 * 	halant, position is moved after it.
		 */


		if ($start + 1 < $end && $start < $base) { /* Otherwise there can't be any pre-base matra characters. */
			/* If we lost track of base, alas, position before last thingy. */
			$new_pos = ($base == $end) ? $base - 2 : $base - 1;

			/* Malayalam / Tamil do not have "half" forms or explicit virama forms.
			 * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
			 * We want to position matra after them.
			 */
			if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
				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)))))
					$new_pos--;

				/* If we found no Halant we are done.
				 * Otherwise only proceed if the Halant does
				 * not belong to the Matra itself! */
				if (self::is_halant_or_coeng($info[$new_pos]) && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
					/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
					if ($new_pos + 1 < $end && self::is_joiner($info[$new_pos + 1]))
						$new_pos++;
				} else
					$new_pos = $start; /* No move. */
			}

			if ($start < $new_pos && $info[$new_pos]['indic_position'] != self::POS_PRE_M) {
				/* Now go see if there's actually any matras... */
				for ($i = $new_pos; $i > $start; $i--)
					if ($info[$i - 1]['indic_position'] == self::POS_PRE_M) {
						$old_pos = $i - 1;
						//memmove (&info[$old_pos], &info[$old_pos + 1], ($new_pos - $old_pos) * sizeof ($info[0]));
						self::_move_info_pos($info, $old_pos, $new_pos + 1);

						if ($old_pos < $base && $base <= $new_pos) /* Shouldn't actually happen. */
							$base--;
						$new_pos--;
					}
			}
		}


		/* 	o Reorder reph:
		 *
		 * 	Reph's original position is always at the beginning of the syllable,
		 * 	(i.e. it is not reordered at the character reordering stage). However,
		 * 	it will be reordered according to the basic-forms shaping results.
		 * 	Possible positions for reph, depending on the script, are; after main,
		 * 	before post-base consonant forms, and after post-base consonant forms.
		 */

		/* If there's anything after the Ra that has the REPH pos, it ought to be halant.
		 * Which means that the font has failed to ligate the Reph.	In which case, we
		 * shouldn't move. */
		if ($start + 1 < $end &&
			$info[$start]['indic_position'] == self::POS_RA_TO_BECOME_REPH && $info[$start + 1]['indic_position'] != self::POS_RA_TO_BECOME_REPH) {
			$reph_pos = $indic_config[3];
			$skip_to_reph_step_5 = false;
			$skip_to_reph_move = false;

			/* 	1. If reph should be positioned after post-base consonant forms,
			 * 	proceed to step 5.
			 */
			if ($reph_pos == self::REPH_POS_AFTER_POST) {
				$skip_to_reph_step_5 = true;
			}

			/* 	2. If the reph repositioning class is not after post-base: target
			 * 	position is after the first explicit halant glyph between the
			 * 	first post-reph consonant and last main consonant. If ZWJ or ZWNJ
			 * 	are following this halant, position is moved after it. If such
			 * 	position is found, this is the target position. Otherwise,
			 * 	proceed to the next step.
			 *
			 * 	Note: in old-implementation fonts, where classifications were
			 * 	fixed in shaping engine, there was no case where reph position
			 * 	will be found on this step.
			 */

			if (!$skip_to_reph_step_5) {

				$new_reph_pos = $start + 1;

				while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
					$new_reph_pos++;

				if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
					/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
					if ($new_reph_pos + 1 < $base && self::is_joiner($info[$new_reph_pos + 1]))
						$new_reph_pos++;
					$skip_to_reph_move = true;
				}
			}

			/* 	3. If reph should be repositioned after the main consonant: find the
			 * 	first consonant not ligated with main, or find the first
			 * 	consonant that is not a potential pre-base reordering Ra.
			 */
			if ($reph_pos == self::REPH_POS_AFTER_MAIN && !$skip_to_reph_move && !$skip_to_reph_step_5) {
				$new_reph_pos = $base;
				/* XXX Skip potential pre-base reordering Ra. */
				while ($new_reph_pos + 1 < $end && $info[$new_reph_pos + 1]['indic_position'] <= self::POS_AFTER_MAIN)
					$new_reph_pos++;
				if ($new_reph_pos < $end)
					$skip_to_reph_move = true;
			}

			/* 	4. If reph should be positioned before post-base consonant, find
			 * 	first post-base classified consonant not ligated with main. If no
			 * 	consonant is found, the target position should be before the
			 * 	first matra, syllable modifier sign or vedic sign.
			 */
			/* This is our take on what step 4 is trying to say (and failing, BADLY). */
			if ($reph_pos == self::REPH_POS_AFTER_SUB && !$skip_to_reph_move && !$skip_to_reph_step_5) {
				$new_reph_pos = $base;
				while ($new_reph_pos < $end && isset($info[$new_reph_pos + 1]['indic_position']) &&
				!( 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)))) {
					$new_reph_pos++;
				}
				if ($new_reph_pos < $end) {
					$skip_to_reph_move = true;
				}
			}

			/* 	5. If no consonant is found in steps 3 or 4, move reph to a position
			 * 		immediately before the first post-base matra, syllable modifier
			 * 		sign or vedic sign that has a reordering class after the intended
			 * 		reph position. For example, if the reordering position for reph
			 * 		is post-main, it will skip above-base matras that also have a
			 * 		post-main position.
			 */
			if (!$skip_to_reph_move) {
				/* Copied from step 2. */
				$new_reph_pos = $start + 1;
				while ($new_reph_pos < $base && !self::is_halant_or_coeng($info[$new_reph_pos]))
					$new_reph_pos++;

				if ($new_reph_pos < $base && self::is_halant_or_coeng($info[$new_reph_pos])) {
					/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
					if ($new_reph_pos + 1 < $base && self::is_joiner($info[$new_reph_pos + 1]))
						$new_reph_pos++;
					$skip_to_reph_move = true;
				}
			}


			/* 	6. Otherwise, reorder reph to the end of the syllable.
			 */
			if (!$skip_to_reph_move) {
				$new_reph_pos = $end - 1;
				while ($new_reph_pos > $start && $info[$new_reph_pos]['indic_position'] == self::POS_SMVD)
					$new_reph_pos--;

				/*
				 * If the Reph is to be ending up after a Matra,Halant sequence,
				 * position it before that Halant so it can interact with the Matra.
				 * However, if it's a plain Consonant,Halant we shouldn't do that.
				 * Uniscribe doesn't do this.
				 * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
				 */
				//if (!$hb_options.uniscribe_bug_compatible && self::is_halant_or_coeng($info[$new_reph_pos])) {
				if (self::is_halant_or_coeng($info[$new_reph_pos])) {
					for ($i = $base + 1; $i < $new_reph_pos; $i++)
						if ($info[$i]['indic_category'] == self::OT_M) {
							/* Ok, got it. */
							$new_reph_pos--;
						}
				}
			}


			/* Move */
			self::_move_info_pos($info, $start, $new_reph_pos + 1);

			if ($start < $base && $base <= $new_reph_pos) {
				$base--;
			}
		}


		/* 	o Reorder pre-base reordering consonants:
		 *
		 * 	If a pre-base reordering consonant is found, reorder it according to
		 * 	the following rules:
		 */


		if (count($GSUBdata['pref']) && $base + 1 < $end) { /* Otherwise there can't be any pre-base reordering Ra. */
			for ($i = $base + 1; $i < $end; $i++) {
				if ($info[$i]['mask'] & self::FLAG(self::PREF)) {
					/* 	1. Only reorder a glyph produced by substitution during application
					 * 	of the <pref> feature. (Note that a font may shape a Ra consonant with
					 * 	the feature generally but block it in certain contexts.)
					 */
// ??? Need to TEST if actual substitution has occurred
					if ($i + 1 == $end || ($info[$i + 1]['mask'] & self::FLAG(self::PREF)) == 0) {
						/*
						 * 	2. Try to find a target position the same way as for pre-base matra.
						 * 	If it is found, reorder pre-base consonant glyph.
						 *
						 * 	3. If position is not found, reorder immediately before main
						 * 	consonant.
						 */
						$new_pos = $base;
						/* Malayalam / Tamil do not have "half" forms or explicit virama forms.
						 * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
						 * We want to position matra after them.
						 */
						if ($scriptblock != UCDN::SCRIPT_MALAYALAM && $scriptblock != UCDN::SCRIPT_TAMIL) {
							while ($new_pos > $start &&
							!(self::is_one_of($info[$new_pos - 1], self::FLAG(self::OT_M) | self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng))))
								$new_pos--;

							/* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a
							 * split matra, it should be reordered to *before* the left part of such matra. */
							if ($new_pos > $start && $info[$new_pos - 1]['indic_category'] == self::OT_M) {
								$old_pos = i;
								for ($i = $base + 1; $i < $old_pos; $i++)
									if ($info[$i]['indic_category'] == self::OT_M) {
										$new_pos--;
										break;
									}
							}
						}

						if ($new_pos > $start && self::is_halant_or_coeng($info[$new_pos - 1])) {
							/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
							if ($new_pos < $end && self::is_joiner($info[$new_pos]))
								$new_pos++;
						}

						$old_pos = $i;
						self::_move_info_pos($info, $old_pos, $new_pos);

						if ($new_pos <= $base && $base < $old_pos)
							$base++;
					}

					break;
				}
			}
		}


		/* Apply 'init' to the Left Matra if it's a word start. */
		if ($info[$start]['indic_position'] == self::POS_PRE_M &&
			($start == 0 ||
			($info[$start - 1]['general_category'] < UCDN::UNICODE_GENERAL_CATEGORY_FORMAT || $info[$start - 1]['general_category'] > UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
			)) {
			$info[$start]['mask'] |= self::FLAG(self::INIT);
		}


		/*
		 * Finish off and go home!
		 */
	}

	public static function _move_info_pos(&$info, $from, $to)
	{
		$t = array();
		$t[0] = $info[$from];
		if ($from > $to) {
			array_splice($info, $from, 1);
			array_splice($info, $to, 0, $t);
		} else {
			array_splice($info, $to, 0, $t);
			array_splice($info, $from, 1);
		}
	}

	public static $ra_chars = array(
		0x0930 => 1, /* Devanagari */
		0x09B0 => 1, /* Bengali */
		0x09F0 => 1, /* Bengali (Assamese) */
		0x0A30 => 1, /* Gurmukhi */ /* No Reph */
		0x0AB0 => 1, /* Gujarati */
		0x0B30 => 1, /* Oriya */
		0x0BB0 => 1, /* Tamil */ /* No Reph */
		0x0C30 => 1, /* Telugu */ /* Reph formed only with ZWJ */
		0x0CB0 => 1, /* Kannada */
		0x0D30 => 1, /* Malayalam */ /* No Reph, Logical Repha */
		0x0DBB => 1, /* Sinhala */ /* Reph formed only with ZWJ */
		0x179A => 1, /* Khmer */ /* No Reph, Visual Repha */
	);

	public static function is_ra($u)
	{
		if (isset(self::$ra_chars[$u]))
			return true;
		return false;
	}

	public static function is_one_of($info, $flags)
	{
		if (isset($info['is_ligature']) && $info['is_ligature'])
			return false; /* If it ligated, all bets are off. */
		return !!(self::FLAG($info['indic_category']) & $flags);
	}

	public static function is_joiner($info)
	{
		return self::is_one_of($info, (self::FLAG(self::OT_ZWJ) | self::FLAG(self::OT_ZWNJ)));
	}

	/* Vowels and placeholders treated as if they were consonants. */

	public static function is_consonant($info)
	{
		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)));
	}

	public static function is_halant_or_coeng($info)
	{
		return self::is_one_of($info, (self::FLAG(self::OT_H) | self::FLAG(self::OT_Coeng)));
	}

	// From hb-private.hh
	public static function in_range($u, $lo, $hi)
	{
		if ((($lo ^ $hi) & $lo) == 0 && (($lo ^ $hi) & $hi) == ($lo ^ $hi) && (($lo ^ $hi) & (($lo ^ $hi) + 1)) == 0)
			return ($u & ~($lo ^ $hi)) == $lo;
		else
			return $lo <= $u && $u <= $hi;
	}

	// From hb-private.hh
	public static function FLAG($x)
	{
		return (1 << ($x));
	}

	// BELOW from hb-ot-shape-complex-indic.cc

	/*
	 * Indic configurations.
	 */

	// base_position
	const BASE_POS_FIRST = 0;
	const BASE_POS_LAST = 1;

	// reph_position
	const REPH_POS_DEFAULT = 10; // POS_BEFORE_POST,

	const REPH_POS_AFTER_MAIN = 5; // POS_AFTER_MAIN,

	const REPH_POS_BEFORE_SUB = 7; // POS_BEFORE_SUB,
	const REPH_POS_AFTER_SUB = 9; // POS_AFTER_SUB,
	const REPH_POS_BEFORE_POST = 10; // POS_BEFORE_POST,
	const REPH_POS_AFTER_POST = 12; // POS_AFTER_POST

	// reph_mode
	const REPH_MODE_IMPLICIT = 0;  /* Reph formed out of initial Ra,H sequence. */
	const REPH_MODE_EXPLICIT = 1;  /* Reph formed out of initial Ra,H,ZWJ sequence. */
	const REPH_MODE_VIS_REPHA = 2; /* Encoded Repha character, no reordering needed. */
	const REPH_MODE_LOG_REPHA = 3; /* Encoded Repha character, needs reordering. */

	/*
	  struct of indic_configs{
	  KEY - script;
	  0 - has_old_spec;
	  1 - virama;
	  2 - base_pos;
	  3 - reph_pos;
	  4 - reph_mode;
	  };
	 */

	public static $indic_configs = array(/* index is SCRIPT_number from UCDN */
		9 => array(true, 0x094D, 1, 10, 0),
		10 => array(true, 0x09CD, 1, 9, 0),
		11 => array(true, 0x0A4D, 1, 7, 0),
		12 => array(true, 0x0ACD, 1, 10, 0),
		13 => array(true, 0x0B4D, 1, 5, 0),
		14 => array(true, 0x0BCD, 1, 12, 0),
		15 => array(true, 0x0C4D, 1, 12, 1),
		16 => array(true, 0x0CCD, 1, 12, 0),
		17 => array(true, 0x0D4D, 1, 5, 3),
		18 => array(false, 0x0DCA, 0, 5, 1), /* Sinhala */
		30 => array(false, 0x17D2, 0, 10, 2), /* Khmer */
		84 => array(false, 0xA9C0, 1, 10, 0), /* Javanese */
	);



	/*

	  // from "hb-ot-shape-complex-indic-table.cc"


	  const ISC_A	 = 0; //	INDIC_SYLLABIC_CATEGORY_AVAGRAHA		Avagraha
	  const ISC_Bi = 8; //	INDIC_SYLLABIC_CATEGORY_BINDU			Bindu
	  const ISC_C	 = 1; //	INDIC_SYLLABIC_CATEGORY_CONSONANT		Consonant
	  const ISC_CD = 1; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD		Consonant_Dead
	  const ISC_CF = 17; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL		Consonant_Final
	  const ISC_CHL = 1; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	Consonant_Head_Letter
	  const ISC_CM = 17; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL		Consonant_Medial
	  const ISC_CP = 11; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	Consonant_Placeholder
	  const ISC_CR = 15; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA		Consonant_Repha
	  const ISC_CS = 1; //	INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	Consonant_Subjoined
	  const ISC_ML = 0; //	INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	Modifying_Letter
	  const ISC_N	 = 3; //	INDIC_SYLLABIC_CATEGORY_NUKTA			Nukta
	  const ISC_x	 = 0; //	INDIC_SYLLABIC_CATEGORY_OTHER			Other
	  const ISC_RS = 13; //	INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	Register_Shifter
	  const ISC_TL = 0; //	INDIC_SYLLABIC_CATEGORY_TONE_LETTER		Tone_Letter
	  const ISC_TM = 3; //	INDIC_SYLLABIC_CATEGORY_TONE_MARK		Tone_Mark
	  const ISC_V	 = 4; //	INDIC_SYLLABIC_CATEGORY_VIRAMA		Virama
	  const ISC_Vs = 8; //	INDIC_SYLLABIC_CATEGORY_VISARGA		Visarga
	  const ISC_Vo = 2; //	INDIC_SYLLABIC_CATEGORY_VOWEL			Vowel
	  const ISC_M	 = 7; //	INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT	Vowel_Dependent
	  const ISC_VI = 2; //	INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	Vowel_Independent

	  const IMC_B	 = 8; //	INDIC_MATRA_CATEGORY_BOTTOM			Bottom
	  const IMC_BR = 11; //	INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT	Bottom_And_Right
	  const IMC_I	 = 15; //	INDIC_MATRA_CATEGORY_INVISIBLE		Invisible
	  const IMC_L	 = 3; //	INDIC_MATRA_CATEGORY_LEFT			Left
	  const IMC_LR = 11; //	INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		Left_And_Right
	  const IMC_x	 = 15; //	INDIC_MATRA_CATEGORY_NOT_APPLICABLE		Not_Applicable
	  const IMC_O	 = 5; //	INDIC_MATRA_CATEGORY_OVERSTRUCK		Overstruck
	  const IMC_R	 = 11; //	INDIC_MATRA_CATEGORY_RIGHT			Right
	  const IMC_T	 = 6; //	INDIC_MATRA_CATEGORY_TOP			Top
	  const IMC_TB = 8; //	INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		Top_And_Bottom
	  const IMC_TBR = 11; //	INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	Top_And_Bottom_And_Right
	  const IMC_TL = 6; //	INDIC_MATRA_CATEGORY_TOP_AND_LEFT		Top_And_Left
	  const IMC_TLR = 11; //	INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	Top_And_Left_And_Right
	  const IMC_TR = 11; //	INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		Top_And_Right
	  const IMC_VOL = 2; //	INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT		Visual_Order_Left

	  If in original table = _(C,x), that = ISC_C,IMC_x
	  Value is IMC_x << 8 (or IMC_x * 256) = 3840
	  plus ISC_C = 1, so = 3841

	 */

	public static $indic_table = array(
		/* Devanagari  (0900..097F) */

		/* 0900 */ 3848, 3848, 3848, 3848, 3842, 3842, 3842, 3842,
		/* 0908 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
		/* 0910 */ 3842, 3842, 3842, 3842, 3842, 3841, 3841, 3841,
		/* 0918 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0920 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0928 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0930 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0938 */ 3841, 3841, 1543, 2823, 3843, 3840, 2823, 775,
		/* 0940 */ 2823, 2055, 2055, 2055, 2055, 1543, 1543, 1543,
		/* 0948 */ 1543, 2823, 2823, 2823, 2823, 2052, 775, 2823,
		/* 0950 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 2055,
		/* 0958 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0960 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0968 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0970 */ 3840, 3840, 3842, 3842, 3842, 3842, 3842, 3842,
		/* 0978 */ 3840, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* Bengali  (0980..09FF) */

		/* 0980 */ 3840, 3848, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0988 */ 3842, 3842, 3842, 3842, 3842, 3840, 3840, 3842,
		/* 0990 */ 3842, 3840, 3840, 3842, 3842, 3841, 3841, 3841,
		/* 0998 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 09A0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 09A8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 09B0 */ 3841, 3840, 3841, 3840, 3840, 3840, 3841, 3841,
		/* 09B8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
		/* 09C0 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
		/* 09C8 */ 775, 3840, 3840, 2823, 2823, 2052, 3841, 3840,
		/* 09D0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
		/* 09D8 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
		/* 09E0 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 09E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 09F0 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 09F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Gurmukhi  (0A00..0A7F) */

		/* 0A00 */ 3840, 3848, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0A08 */ 3842, 3842, 3842, 3840, 3840, 3840, 3840, 3842,
		/* 0A10 */ 3842, 3840, 3840, 3842, 3842, 3841, 3841, 3841,
		/* 0A18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0A20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0A28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0A30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3840,
		/* 0A38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
		/* 0A40 */ 2823, 2055, 2055, 3840, 3840, 3840, 3840, 1543,
		/* 0A48 */ 1543, 3840, 3840, 1543, 1543, 2052, 3840, 3840,
		/* 0A50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0A58 */ 3840, 3841, 3841, 3841, 3841, 3840, 3841, 3840,
		/* 0A60 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0A68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0A70 */ 3848, 3840, 13841, 13841, 3840, 3857, 3840, 3840,
		/* 0A78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Gujarati  (0A80..0AFF) */

		/* 0A80 */ 3840, 3848, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0A88 */ 3842, 3842, 3842, 3842, 3842, 3842, 3840, 3842,
		/* 0A90 */ 3842, 3842, 3840, 3842, 3842, 3841, 3841, 3841,
		/* 0A98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0AA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0AA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0AB0 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
		/* 0AB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 775,
		/* 0AC0 */ 2823, 2055, 2055, 2055, 2055, 1543, 3840, 1543,
		/* 0AC8 */ 1543, 2823, 3840, 2823, 2823, 2052, 3840, 3840,
		/* 0AD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0AD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0AE0 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0AE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0AF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0AF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Oriya  (0B00..0B7F) */

		/* 0B00 */ 3840, 3848, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0B08 */ 3842, 3842, 3842, 3842, 3842, 3840, 3840, 3842,
		/* 0B10 */ 3842, 3840, 3840, 3842, 3842, 3841, 3841, 3841,
		/* 0B18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0B20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0B28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0B30 */ 3841, 3840, 3841, 3841, 3840, 3841, 3841, 3841,
		/* 0B38 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
		/* 0B40 */ 2823, 2055, 2055, 2055, 2055, 3840, 3840, 775,
		/* 0B48 */ 1543, 3840, 3840, 2823, 2823, 2052, 3840, 3840,
		/* 0B50 */ 3840, 3840, 3840, 3840, 3840, 3840, 1543, 2823,
		/* 0B58 */ 3840, 3840, 3840, 3840, 3841, 3841, 3840, 3841,
		/* 0B60 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0B68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0B70 */ 3840, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0B78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Tamil  (0B80..0BFF) */

		/* 0B80 */ 3840, 3840, 3848, 3840, 3840, 3842, 3842, 3842,
		/* 0B88 */ 3842, 3842, 3842, 3840, 3840, 3840, 3842, 3842,
		/* 0B90 */ 3842, 3840, 3842, 3842, 3842, 3841, 3840, 3840,
		/* 0B98 */ 3840, 3841, 3841, 3840, 3841, 3840, 3841, 3841,
		/* 0BA0 */ 3840, 3840, 3840, 3841, 3841, 3840, 3840, 3840,
		/* 0BA8 */ 3841, 3841, 3841, 3840, 3840, 3840, 3841, 3841,
		/* 0BB0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0BB8 */ 3841, 3841, 3840, 3840, 3840, 3840, 2823, 2823,
		/* 0BC0 */ 1543, 2055, 2055, 3840, 3840, 3840, 775, 775,
		/* 0BC8 */ 775, 3840, 2823, 2823, 2823, 1540, 3840, 3840,
		/* 0BD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
		/* 0BD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0BE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0BE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0BF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0BF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Telugu  (0C00..0C7F) */

		/* 0C00 */ 3840, 3848, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0C08 */ 3842, 3842, 3842, 3842, 3842, 3840, 3842, 3842,
		/* 0C10 */ 3842, 3840, 3842, 3842, 3842, 3841, 3841, 3841,
		/* 0C18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0C20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0C28 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0C30 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
		/* 0C38 */ 3841, 3841, 3840, 3840, 3840, 3840, 1543, 1543,
		/* 0C40 */ 1543, 2823, 2823, 2823, 2823, 3840, 1543, 1543,
		/* 0C48 */ 2055, 3840, 1543, 1543, 1543, 1540, 3840, 3840,
		/* 0C50 */ 3840, 3840, 3840, 3840, 3840, 1543, 2055, 3840,
		/* 0C58 */ 3841, 3841, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0C60 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0C68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0C70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0C78 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Kannada  (0C80..0CFF) */

		/* 0C80 */ 3840, 3840, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0C88 */ 3842, 3842, 3842, 3842, 3842, 3840, 3842, 3842,
		/* 0C90 */ 3842, 3840, 3842, 3842, 3842, 3841, 3841, 3841,
		/* 0C98 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0CA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0CA8 */ 3841, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0CB0 */ 3841, 3841, 3841, 3841, 3840, 3841, 3841, 3841,
		/* 0CB8 */ 3841, 3841, 3840, 3840, 3843, 3840, 2823, 1543,
		/* 0CC0 */ 2823, 2823, 2823, 2823, 2823, 3840, 1543, 2823,
		/* 0CC8 */ 2823, 3840, 2823, 2823, 1543, 1540, 3840, 3840,
		/* 0CD0 */ 3840, 3840, 3840, 3840, 3840, 2823, 2823, 3840,
		/* 0CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3841, 3840,
		/* 0CE0 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0CF0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Malayalam  (0D00..0D7F) */

		/* 0D00 */ 3840, 3840, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0D08 */ 3842, 3842, 3842, 3842, 3842, 3840, 3842, 3842,
		/* 0D10 */ 3842, 3840, 3842, 3842, 3842, 3841, 3841, 3841,
		/* 0D18 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0D20 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0D28 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0D30 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0D38 */ 3841, 3841, 3841, 3840, 3840, 3840, 2823, 2823,
		/* 0D40 */ 2823, 2823, 2823, 2055, 2055, 3840, 775, 775,
		/* 0D48 */ 775, 3840, 2823, 2823, 2823, 1540, 3855, 3840,
		/* 0D50 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 2823,
		/* 0D58 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0D60 */ 3842, 3842, 2055, 2055, 3840, 3840, 3840, 3840,
		/* 0D68 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0D70 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0D78 */ 3840, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* Sinhala  (0D80..0DFF) */

		/* 0D80 */ 3840, 3840, 3848, 3848, 3840, 3842, 3842, 3842,
		/* 0D88 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
		/* 0D90 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3840,
		/* 0D98 */ 3840, 3840, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0DA0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0DA8 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 0DB0 */ 3841, 3841, 3840, 3841, 3841, 3841, 3841, 3841,
		/* 0DB8 */ 3841, 3841, 3841, 3841, 3840, 3841, 3840, 3840,
		/* 0DC0 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3840,
		/* 0DC8 */ 3840, 3840, 1540, 3840, 3840, 3840, 3840, 2823,
		/* 0DD0 */ 2823, 2823, 1543, 1543, 2055, 3840, 2055, 3840,
		/* 0DD8 */ 2823, 775, 1543, 775, 2823, 2823, 2823, 2823,
		/* 0DE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0DE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 0DF0 */ 3840, 3840, 2823, 2823, 3840, 3840, 3840, 3840,
		/* 0DF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* Vedic Extensions  (1CD0..1CFF) */

		/* 1CD0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 1CD8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 1CE0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 1CE8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 1CF0 */ 3840, 3840, 3848, 3848, 3840, 3840, 3840, 3840,
		/* 1CF8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
	);

	public static $khmer_table = array(
		/* Khmer  (1780..17FF) */

		/* 1780 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 1788 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 1790 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 1798 */ 3841, 3841, 3841, 3841, 3841, 3841, 3841, 3841,
		/* 17A0 */ 3841, 3841, 3841, 3842, 3842, 3842, 3842, 3842,
		/* 17A8 */ 3842, 3842, 3842, 3842, 3842, 3842, 3842, 3842,
		/* 17B0 */ 3842, 3842, 3842, 3842, 3840, 3840, 2823, 1543,
		/* 17B8 */ 1543, 1543, 1543, 2055, 2055, 2055, 1543, 2823,
		/* 17C0 */ 2823, 775, 775, 775, 2823, 2823, 3848, 3848,
		/* 17C8 */ 2823, 3853, 3853, 3840, 3855, 3840, 3840, 3840,
		/* 17D0 */ 3840, 1540, 3844, 3840, 3840, 3840, 3840, 3840,
		/* 17D8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 17E0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 17E8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 17F0 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
		/* 17F8 */ 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840,
	);

	// from "hb-ot-shape-complex-indic-table.cc"
	public static function indic_get_categories($u)
	{
		if (0x0900 <= $u && $u <= 0x0DFF)
			return self::$indic_table[$u - 0x0900 + 0]; // offset 0 for Most "indic"
		if (0x1CD0 <= $u && $u <= 0x1D00)
			return self::$indic_table[$u - 0x1CD0 + 1152]; // offset for Vedic extensions
		if (0x1780 <= $u && $u <= 0x17FF)
			return self::$khmer_table[$u - 0x1780];  // Khmer
		if ($u == 0x00A0)
			return 3851; // (ISC_CP | (IMC_x << 8))
		if ($u == 0x25CC)
			return 3851; // (ISC_CP | (IMC_x << 8))
		return 3840; // (ISC_x | (IMC_x << 8))
	}

	// BELOW from hb-ot-shape-complex-indic.cc
	/*
	 * Indic shaper.
	 */

	public static function IN_HALF_BLOCK($u, $Base)
	{
		return (($u & ~0x7F) == $Base);
	}

	public static function IS_DEVA($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0900);
	}

	public static function IS_BENG($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0980);
	}

	public static function IS_GURU($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0A00);
	}

	public static function IS_GUJR($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0A80);
	}

	public static function IS_ORYA($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0B00);
	}

	public static function IS_TAML($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0B80);
	}

	public static function IS_TELU($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0C00);
	}

	public static function IS_KNDA($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0C80);
	}

	public static function IS_MLYM($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0D00);
	}

	public static function IS_SINH($u)
	{
		return self::IN_HALF_BLOCK($u, 0x0D80);
	}

	public static function IS_KHMR($u)
	{
		return self::IN_HALF_BLOCK($u, 0x1780);
	}

	public static function MATRA_POS_LEFT($u)
	{
		return self::POS_PRE_M;
	}

	public static function MATRA_POS_RIGHT($u)
	{
		return
			(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
				(self::IS_BENG($u) ? self::POS_AFTER_POST :
					(self::IS_GURU($u) ? self::POS_AFTER_POST :
						(self::IS_GUJR($u) ? self::POS_AFTER_POST :
							(self::IS_ORYA($u) ? self::POS_AFTER_POST :
								(self::IS_TAML($u) ? self::POS_AFTER_POST :
									(self::IS_TELU($u) ? ($u <= 0x0C42 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
										(self::IS_KNDA($u) ? ($u < 0x0CC3 || $u > 0xCD6 ? self::POS_BEFORE_SUB : self::POS_AFTER_SUB) :
											(self::IS_MLYM($u) ? self::POS_AFTER_POST :
												(self::IS_SINH($u) ? self::POS_AFTER_SUB :
													(self::IS_KHMR($u) ? self::POS_AFTER_POST :
														self::POS_AFTER_SUB))))))))))); /* default */
	}

	public static function MATRA_POS_TOP($u)
	{
		return /* BENG and MLYM don't have top matras. */
			(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
				(self::IS_GURU($u) ? self::POS_AFTER_POST : /* Deviate from spec */
					(self::IS_GUJR($u) ? self::POS_AFTER_SUB :
						(self::IS_ORYA($u) ? self::POS_AFTER_MAIN :
							(self::IS_TAML($u) ? self::POS_AFTER_SUB :
								(self::IS_TELU($u) ? self::POS_BEFORE_SUB :
									(self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
										(self::IS_SINH($u) ? self::POS_AFTER_SUB :
											(self::IS_KHMR($u) ? self::POS_AFTER_POST :
												self::POS_AFTER_SUB))))))))); /* default */
	}

	public static function MATRA_POS_BOTTOM($u)
	{
		return
			(self::IS_DEVA($u) ? self::POS_AFTER_SUB :
				(self::IS_BENG($u) ? self::POS_AFTER_SUB :
					(self::IS_GURU($u) ? self::POS_AFTER_POST :
						(self::IS_GUJR($u) ? self::POS_AFTER_POST :
							(self::IS_ORYA($u) ? self::POS_AFTER_SUB :
								(self::IS_TAML($u) ? self::POS_AFTER_POST :
									(self::IS_TELU($u) ? self::POS_BEFORE_SUB :
										(self::IS_KNDA($u) ? self::POS_BEFORE_SUB :
											(self::IS_MLYM($u) ? self::POS_AFTER_POST :
												(self::IS_SINH($u) ? self::POS_AFTER_SUB :
													(self::IS_KHMR($u) ? self::POS_AFTER_POST :
														self::POS_AFTER_SUB))))))))))); /* default */
	}

	public static function matra_position($u, $side)
	{
		switch ($side) {
			case self::POS_PRE_C: return self::MATRA_POS_LEFT($u);
			case self::POS_POST_C: return self::MATRA_POS_RIGHT($u);
			case self::POS_ABOVE_C: return self::MATRA_POS_TOP($u);
			case self::POS_BELOW_C: return self::MATRA_POS_BOTTOM($u);
		}
		return $side;
	}

	// vowel matras that have to be split into two parts.
	// From Harfbuzz (old)
	// New HarfBuzz uses /src/hb-ucdn/ucdn.c and unicodedata_db.h for full method of decomposition for all characters
	// Should always fully decompose and then recompose back, but we will just do the split matras
	public static function decompose_indic($ab)
	{
		$sub = array();
		switch ($ab) {
			/*
			 * Decompose split matras.
			 */
			/* bengali */
			case 0x9cb : $sub[0] = 0x9c7;
				$sub[1] = 0x9be;
				return $sub;
			case 0x9cc : $sub[0] = 0x9c7;
				$sub[1] = 0x9d7;
				return $sub;
			/* oriya */
			case 0xb48 : $sub[0] = 0xb47;
				$sub[1] = 0xb56;
				return $sub;
			case 0xb4b : $sub[0] = 0xb47;
				$sub[1] = 0xb3e;
				return $sub;
			case 0xb4c : $sub[0] = 0xb47;
				$sub[1] = 0xb57;
				return $sub;
			/* tamil */
			case 0xbca : $sub[0] = 0xbc6;
				$sub[1] = 0xbbe;
				return $sub;
			case 0xbcb : $sub[0] = 0xbc7;
				$sub[1] = 0xbbe;
				return $sub;
			case 0xbcc : $sub[0] = 0xbc6;
				$sub[1] = 0xbd7;
				return $sub;
			/* telugu */
			case 0xc48 : $sub[0] = 0xc46;
				$sub[1] = 0xc56;
				return $sub;
			/* kannada */
			case 0xcc0 : $sub[0] = 0xcbf;
				$sub[1] = 0xcd5;
				return $sub;
			case 0xcc7 : $sub[0] = 0xcc6;
				$sub[1] = 0xcd5;
				return $sub;
			case 0xcc8 : $sub[0] = 0xcc6;
				$sub[1] = 0xcd6;
				return $sub;
			case 0xcca : $sub[0] = 0xcc6;
				$sub[1] = 0xcc2;
				return $sub;
			case 0xccb : $sub[0] = 0xcc6;
				$sub[1] = 0xcc2;
				$sub[2] = 0xcd5;
				return $sub;
			/* malayalam */
			case 0xd4a : $sub[0] = 0xd46;
				$sub[1] = 0xd3e;
				return $sub;
			case 0xd4b : $sub[0] = 0xd47;
				$sub[1] = 0xd3e;
				return $sub;
			case 0xd4c : $sub[0] = 0xd46;
				$sub[1] = 0xd57;
				return $sub;
			/* sinhala */
			// NB Some fonts break with these Sinhala decomps (although this is Uniscribe spec)
			// Can check if character would be substituted by pstf and only decompose if true
			// e.g. if (isset($GSUBdata['pstf'][$ab])) - would need to pass $GSUBdata as parameter to this function
			case 0xdda : $sub[0] = 0xdd9;
				$sub[1] = 0xdca;
				return $sub;
			case 0xddc : $sub[0] = 0xdd9;
				$sub[1] = 0xdcf;
				return $sub;
			case 0xddd : $sub[0] = 0xdd9;
				$sub[1] = 0xdcf;
				$sub[2] = 0xdca;
				return $sub;
			case 0xdde : $sub[0] = 0xdd9;
				$sub[1] = 0xddf;
				return $sub;
			/* khmer */
			case 0x17be : $sub[0] = 0x17c1;
				$sub[1] = 0x17be;
				return $sub;
			case 0x17bf : $sub[0] = 0x17c1;
				$sub[1] = 0x17bf;
				return $sub;
			case 0x17c0 : $sub[0] = 0x17c1;
				$sub[1] = 0x17c0;
				return $sub;

			case 0x17c4 : $sub[0] = 0x17c1;
				$sub[1] = 0x17c4;
				return $sub;
			case 0x17c5 : $sub[0] = 0x17c1;
				$sub[1] = 0x17c5;
				return $sub;
			/* tibetan - included here although does not use Inidc shaper in other ways  */
			case 0xf73 : $sub[0] = 0xf71;
				$sub[1] = 0xf72;
				return $sub;
			case 0xf75 : $sub[0] = 0xf71;
				$sub[1] = 0xf74;
				return $sub;
			case 0xf76 : $sub[0] = 0xfb2;
				$sub[1] = 0xf80;
				return $sub;
			case 0xf77 : $sub[0] = 0xfb2;
				$sub[1] = 0xf81;
				return $sub;
			case 0xf78 : $sub[0] = 0xfb3;
				$sub[1] = 0xf80;
				return $sub;
			case 0xf79 : $sub[0] = 0xfb3;
				$sub[1] = 0xf71;
				$sub[2] = 0xf80;
				return $sub;
			case 0xf81 : $sub[0] = 0xf71;
				$sub[1] = 0xf80;
				return $sub;
		}
		return false;
	}

	public static function bubble_sort(&$arr, $start, $len)
	{
		if ($len < 2) {
			return;
		}
		$k = $start + $len - 2;
		while ($k >= $start) {
			for ($j = $start; $j <= $k; $j++) {
				if ($arr[$j]['indic_position'] > $arr[$j + 1]['indic_position']) {
					$t = $arr[$j];
					$arr[$j] = $arr[$j + 1];
					$arr[$j + 1] = $t;
				}
			}
			$k--;
		}
	}

}

Youez - 2016 - github.com/yon3zu
LinuXploit