From df32eaae42b505b00de4a8b5efce9ab948bed847 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Oct 2018 14:44:25 +0200 Subject: [PATCH] [indic] Disallow vowel mark combinations that spoof other vowel marks Fixes https://github.com/harfbuzz/harfbuzz/issues/1019 New numbers: BENGALI: 353725 out of 354188 tests passed. 463 failed (0.130722%) DEVANAGARI: 707261 out of 707394 tests passed. 133 failed (0.0188014%) GUJARATI: 366353 out of 366457 tests passed. 104 failed (0.0283799%) GURMUKHI: 60729 out of 60747 tests passed. 18 failed (0.0296311%) KANNADA: 951300 out of 951913 tests passed. 613 failed (0.0643966%) MALAYALAM: 1048136 out of 1048334 tests passed. 198 failed (0.0188871%) ORIYA: 42327 out of 42329 tests passed. 2 failed (0.00472489%) SINHALA: 271596 out of 271847 tests passed. 251 failed (0.0923313%) TAMIL: 1091754 out of 1091754 tests passed. 0 failed (0%) TELUGU: 970555 out of 970573 tests passed. 18 failed (0.00185457%) Devanagari regressed because Uniscribe doesn't enforce the full set. Tests added with the *-vowel-letters.txt files in tree and Noto fonts. --- src/hb-ot-shape-complex-indic.cc | 256 +++++++++++++++++- test/shaping/data/in-house/Makefile.sources | 1 + ...e3f463c3a985bc42096620cc415342818454fb.ttf | Bin 0 -> 2904 bytes ...5face3fcbd929d228235c2f72bbd6f8eb37424.ttf | Bin 0 -> 8188 bytes ...25beb56d9c556622d56b0b5d02b4670c034f89.ttf | Bin 0 -> 2460 bytes ...4026ae5aaca83c49cd8416909d71ba3e1c1194.ttf | Bin 0 -> 4120 bytes ...8d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf | Bin 0 -> 2336 bytes ...18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf | Bin 0 -> 3452 bytes ...1642af1667ae30a54e58de8be904566d00508f.ttf | Bin 0 -> 2760 bytes ...85624080af5627fb050f570d148a62f04fda74.ttf | Bin 0 -> 2656 bytes .../tests/indic-vowel-letter-spoofing.tests | 53 ++++ 11 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf create mode 100644 test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf create mode 100644 test/shaping/data/in-house/fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf create mode 100644 test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf create mode 100644 test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf create mode 100644 test/shaping/data/in-house/fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf create mode 100644 test/shaping/data/in-house/fonts/881642af1667ae30a54e58de8be904566d00508f.ttf create mode 100644 test/shaping/data/in-house/fonts/af85624080af5627fb050f570d148a62f04fda74.ttf create mode 100644 test/shaping/data/in-house/tests/indic-vowel-letter-spoofing.tests diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index c230be8be..d169b2b3f 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -331,6 +331,260 @@ data_destroy_indic (void *data) free (data); } +static void +preprocess_text_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of + * vowel-sequences that look like another vowel. Data for each script + * collected from Unicode 11 book, tables named "Vowel Letters" with + * "Use" and "Do Not Use" columns. + * + * https://github.com/harfbuzz/harfbuzz/issues/1019 + */ + bool processed = false; + buffer->clear_output (); + unsigned int count = buffer->len; + switch ((unsigned) buffer->props.script) + { + case HB_SCRIPT_DEVANAGARI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0905u: + switch (buffer->cur(1).codepoint) + { + case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u: + case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu: + case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u: + matched = true; + break; + } + break; + case 0x0906u: + switch (buffer->cur(1).codepoint) + { + case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u: + case 0x0948u: + matched = true; + break; + } + break; + case 0x0909u: + switch (buffer->cur(1).codepoint) + { + case 0x0941u: + matched = true; + break; + } + break; + case 0x090Fu: + switch (buffer->cur(1).codepoint) + { + case 0x0945u: case 0x0946u: case 0x0947u: + matched = true; + break; + } + break; + case 0x0930u: + if (0x094Du == buffer->cur(1).codepoint && + buffer->idx + 2 < count && + 0x0907u == buffer->cur(2).codepoint) + { + buffer->next_glyph (); + buffer->next_glyph (); + buffer->output_glyph (0x25CCu); + } + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_BENGALI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0985u: matched = 0x09BE == buffer->cur(1).codepoint; break; + case 0x098Bu: matched = 0x09C3 == buffer->cur(1).codepoint; break; + case 0x098Cu: matched = 0x09E2 == buffer->cur(1).codepoint; break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_GURMUKHI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0A05u: + switch (buffer->cur(1).codepoint) + { + case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu: + matched = true; + break; + } + break; + case 0x0A72u: + switch (buffer->cur(1).codepoint) + { + case 0x0A3Fu: case 0x0A40u: case 0x0A47u: + matched = true; + break; + } + break; + case 0x0A73u: + switch (buffer->cur(1).codepoint) + { + case 0x0A41u: case 0x0A42u: case 0x0A4Bu: + matched = true; + break; + } + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_GUJARATI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0A85u: + switch (buffer->cur(1).codepoint) + { + case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u: + case 0x0AC9u: case 0x0ACBu: case 0x0ACCu: + matched = true; + break; + } + break; + case 0x0AC5u: + matched = 0x0ABE == buffer->cur(1).codepoint; break; + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_ORIYA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0B05u: + matched = 0x0B3E == buffer->cur(1).codepoint; + break; + case 0x0B0Fu: case 0x0B13u: + matched = 0x0B57 == buffer->cur(1).codepoint; + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_TELUGU: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0C12u: + switch (buffer->cur(1).codepoint) + { + case 0x0C4Cu: case 0x0C55u: + matched = true; + break; + } + break; + case 0x0C3Fu: case 0x0C46u: case 0xC4Au: + matched = 0x0C55 == buffer->cur(1).codepoint; + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_KANNADA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0C89u: case 0x0C8Bu: + matched = 0x0CBE == buffer->cur(1).codepoint; + break; + case 0x0C92u: + matched = 0x0CCC == buffer->cur(1).codepoint; + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + case HB_SCRIPT_MALAYALAM: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur().codepoint) + { + case 0x0D07u: case 0x0D09u: + matched = 0x0D57 == buffer->cur(1).codepoint; + break; + case 0x0D0Eu: + matched = 0x0D46 == buffer->cur(1).codepoint; + break; + case 0x0D12u: + switch (buffer->cur(1).codepoint) + { + case 0x0D3Eu: case 0x0D57u: + matched = true; + break; + } + break; + } + buffer->next_glyph (); + if (matched) { buffer->output_glyph (0x25CCu); buffer->next_glyph (); } + } + processed = true; + break; + + default: + break; + } + if (processed) + { + if (buffer->idx < count) + buffer->next_glyph (); + if (likely (buffer->successful)) + buffer->swap_buffers (); + } +} + static indic_position_t consonant_position_from_face (const indic_shape_plan_t *indic_plan, const hb_codepoint_t consonant, @@ -1615,7 +1869,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic = override_features_indic, data_create_indic, data_destroy_indic, - nullptr, /* preprocess_text */ + preprocess_text_indic, nullptr, /* postprocess_glyphs */ HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, decompose_indic, diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources index a79ab0413..293bb14f3 100644 --- a/test/shaping/data/in-house/Makefile.sources +++ b/test/shaping/data/in-house/Makefile.sources @@ -27,6 +27,7 @@ TESTS = \ tests/indic-script-extensions.tests \ tests/indic-special-cases.tests \ tests/indic-syllable.tests \ + tests/indic-vowel-letter-spoofing.tests \ tests/khmer-mark-order.tests \ tests/khmer-misc.tests \ tests/language-tags.tests \ diff --git a/test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf b/test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ee540f3bd3cbbe4ac6ddd7f570ef495c25f9194e GIT binary patch literal 2904 zcmb7GdsLI>8GpV@5`>5-7i-5Q5C{=t#e@(`AaYGakf0z&aAF4G;u0<)L1;JesO>48 zJz5o3s8nsY&b75&wJV+f^t9dS_J`fNZfBdloVMq*)Iat|Pr%jENDWvIIL=YGyY%zbpS=Tk z3t)}2Zj*8F$&)9EqFw@gpvC9z+1n6PM6?1t)Y|Uu>me0BivAX`y4^e2X1ni70@nW` zQciVv+^yX2Z~DQ{aQPiTL|^8$fFA>#+2IQe zpoa`xG2p*3xXbPH{9xK_fczdJuC1rLFCcYN8|43!?Zd`OX-Y96NK{BgWCM*UDamBa zH|OcHElEj<2}*5-E?db)b6&nBpKWL}l!*yzRSaP=G7^;%2*#D2#sB7f)WdC!Zs#6f zS8(!@e>C4V_Q*3=PW+fxbF#i?z&}y~$8g-+OW-&^f$iQ-gJ@eekyjh(uXyNxzvcD<*_ zl)Ik4!%*>4Ng_Ssb3@V3XAqBw@yJXfVfL~Rl(euv5iv}M_2B@?@z7=n&AxUOP(vxXX&NBZY9l{P=E*e_QpWc1q=+R7V?8I^AD{Xgq6_=Cw{Ohk>8axR- zdRwy7Tn`qOJe0dzyjs1#*|0rz{l$ zSDHM&c5`M<{%YbwCw&3yvWOjQPG6oE>knGm_P`T3ZbF>*95+6SubR0DpP6u8Np77hwA3Z*@;YU|e&adipq*vDNtS=H&=BlkW`@R;zbMCQ2<2BCu z%5_!!u7^*L{J4JSn~k2bg7RXHvpa27+X`!{kyT8y*Wir@seWt)tw5EGWJg%bbXKjDOA{r1iBD&p zVpH%5zis09u~EbPM~T*vvwy!K=Z+ASCwmgnUAO%ZTzJN}x7t`-I#3nPrHa~!7w5)= zN$DoMxSG~bJao`4_nRetPTFi3+(f z95PY?m4~Cl(nPMossy$&85Q9<{vNPOW<}0%@%+zH(h8DO(^4cW8aL0zeT7zQ;oSDn zk_|Fe0%1W~%KWz?|7mK9H8nZ;INL;{S~WOdcneKm>iS%@QwXm zG0_XsVn{wrw2hv6`PFbpR@^Nln{A~*$0_-GFN6dYlfCJyXcV5jgbK=_#U?JmP4qr} z#Km!q+z|IWUdeaxzvllW7=^wN1*d-Rk4pR5=2eBaC?+bzfBoG*_}JnD{dL8y$=Q;X z65@`>HdL#z5Z|M>_;Mn}4XO-x{5Q;Amiuil5vs{UvrXadB$^u&FVHp6>X}UC%q-si z*pyq7|NiSrX+cjWlC@PNDqY+uQb5AEg4JiIJoDYu#`nYZF$!K(h&(TnCd9EYIGj!g z6_Y0Gx-3IEBYc9*kY@x*MEZdP4*)Pi2&=}c>jJ!n;(+0?dqJI`E>=6#)#}|@*YQ#@ zjtlBF>aA+Kdb^C1K9}6nUr)a~{nqrG(~}>TgiZqCvZkU&CPiEU{C;$K&EdTL;Wc~r z4u{tQYSW4En%NbhQBW#<5XLL<&-OKyQ8)F_Ao;12+NpyApf*x2ZN^)sAqRH5f%lRJ zP$hLy3q62U2{2yFyB4_mLXe00K0Y@o|l3+H3)uBX!c^) zPdh-_72+6#PO?NFcG@AO3!?`0N^{RM8^Gy@q|VT38d&lG>@kBg2>T)$Mf6#!4fATK zNp0-^@2-ufDB=;xNW^6#J_NH{qQIZ8Kr8Wa*kU~z81_fPnhYl+3VguhHBv0b;KDBb E2d^WV*8l(j literal 0 HcmV?d00001 diff --git a/test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf b/test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf new file mode 100644 index 0000000000000000000000000000000000000000..383aee66e7794972a45c4663d689002b6e7e6714 GIT binary patch literal 8188 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 literal 0 HcmV?d00001 diff --git a/test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf b/test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a6f1c9df3c23ff4f76f69edba1cca90661079463 GIT binary patch literal 4120 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 B^vD1J literal 0 HcmV?d00001 diff --git a/test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf b/test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c3e4167fe5c9fe7b6563f69b3b784f97f6d44b68 GIT binary patch literal 2336 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