1 /* 2 ******************************************************************************* 3 * 4 * Copyright (C) 2016 and later: Unicode, Inc. and others. 5 * License & terms of use: http://www.unicode.org/copyright.html#License 6 * 7 ******************************************************************************* 8 ******************************************************************************* 9 * 10 * Copyright (C) 1999-2013, International Business Machines 11 * Corporation and others. All Rights Reserved. 12 * 13 ******************************************************************************* 14 */ 15 16 #include "unicode/utypes.h" 17 #include "unicode/uclean.h" 18 #include "unicode/uchar.h" 19 #include "unicode/unistr.h" 20 #include "unicode/uscript.h" 21 #include "unicode/putil.h" 22 #include "unicode/ctest.h" 23 24 #include "layout/LETypes.h" 25 #include "layout/LEScripts.h" 26 27 #include "letsutil.h" 28 #include "letest.h" 29 30 #include "xmlreader.h" 31 32 #include "xmlparser.h" 33 34 #include <stdlib.h> 35 #include <stdio.h> 36 #include <string.h> 37 38 //U_NAMESPACE_USE 39 40 #define CH_COMMA 0x002C 41 42 static le_uint32 *getHexArray(const UnicodeString &numbers, int32_t &arraySize) 43 { 44 int32_t offset = -1; 45 46 arraySize = 1; 47 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) { 48 arraySize += 1; 49 } 50 51 le_uint32 *array = NEW_ARRAY(le_uint32, arraySize); 52 char number[16]; 53 le_int32 count = 0; 54 le_int32 start = 0, end = 0; 55 le_int32 len = 0; 56 57 // trim leading whitespace 58 while(u_isUWhiteSpace(numbers[start])) { 59 start += 1; 60 } 61 62 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) { 63 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV); 64 number[len] = '\0'; 65 start = end + 1; 66 67 sscanf(number, "%x", &array[count++]); 68 69 // trim whitespace following the comma 70 while(u_isUWhiteSpace(numbers[start])) { 71 start += 1; 72 } 73 } 74 75 // trim trailing whitespace 76 end = numbers.length(); 77 while(u_isUWhiteSpace(numbers[end - 1])) { 78 end -= 1; 79 } 80 81 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV); 82 number[len] = '\0'; 83 sscanf(number, "%x", &array[count]); 84 85 return array; 86 } 87 88 static float *getFloatArray(const UnicodeString &numbers, int32_t &arraySize) 89 { 90 int32_t offset = -1; 91 92 arraySize = 1; 93 while((offset = numbers.indexOf(CH_COMMA, offset + 1)) >= 0) { 94 arraySize += 1; 95 } 96 97 float *array = NEW_ARRAY(float, arraySize); 98 char number[32]; 99 le_int32 count = 0; 100 le_int32 start = 0, end = 0; 101 le_int32 len = 0; 102 103 // trim leading whitespace 104 while(u_isUWhiteSpace(numbers[start])) { 105 start += 1; 106 } 107 108 while((end = numbers.indexOf(CH_COMMA, start)) >= 0) { 109 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV); 110 number[len] = '\0'; 111 start = end + 1; 112 113 sscanf(number, "%f", &array[count++]); 114 115 // trim whiteapce following the comma 116 while(u_isUWhiteSpace(numbers[start])) { 117 start += 1; 118 } 119 } 120 121 while(u_isUWhiteSpace(numbers[start])) { 122 start += 1; 123 } 124 125 // trim trailing whitespace 126 end = numbers.length(); 127 while(u_isUWhiteSpace(numbers[end - 1])) { 128 end -= 1; 129 } 130 131 len = numbers.extract(start, end - start, number, ARRAY_SIZE(number), US_INV); 132 number[len] = '\0'; 133 sscanf(number, "%f", &array[count]); 134 135 return array; 136 } 137 138 U_CDECL_BEGIN 139 void readTestFile(const char *testFilePath, TestCaseCallback callback) 140 { 141 #if !UCONFIG_NO_REGULAR_EXPRESSIONS 142 UErrorCode status = U_ZERO_ERROR; 143 UXMLParser *parser = UXMLParser::createParser(status); 144 UXMLElement *root = parser->parseFile(testFilePath, status); 145 146 if (root == NULL) { 147 log_err("Could not open the test data file: %s\n", testFilePath); 148 delete parser; 149 return; 150 } 151 152 UnicodeString test_case = UNICODE_STRING_SIMPLE("test-case"); 153 UnicodeString test_text = UNICODE_STRING_SIMPLE("test-text"); 154 UnicodeString test_font = UNICODE_STRING_SIMPLE("test-font"); 155 UnicodeString result_glyphs = UNICODE_STRING_SIMPLE("result-glyphs"); 156 UnicodeString result_indices = UNICODE_STRING_SIMPLE("result-indices"); 157 UnicodeString result_positions = UNICODE_STRING_SIMPLE("result-positions"); 158 159 // test-case attributes 160 UnicodeString id_attr = UNICODE_STRING_SIMPLE("id"); 161 UnicodeString script_attr = UNICODE_STRING_SIMPLE("script"); 162 UnicodeString lang_attr = UNICODE_STRING_SIMPLE("lang"); 163 164 // test-font attributes 165 UnicodeString name_attr = UNICODE_STRING_SIMPLE("name"); 166 UnicodeString ver_attr = UNICODE_STRING_SIMPLE("version"); 167 UnicodeString cksum_attr = UNICODE_STRING_SIMPLE("checksum"); 168 169 const UXMLElement *testCase; 170 int32_t tc = 0; 171 172 while((testCase = root->nextChildElement(tc)) != NULL) { 173 if (testCase->getTagName().compare(test_case) == 0) { 174 char *id = getCString(testCase->getAttribute(id_attr)); 175 char *script = getCString(testCase->getAttribute(script_attr)); 176 char *lang = getCString(testCase->getAttribute(lang_attr)); 177 char *fontName = NULL; 178 char *fontVer = NULL; 179 char *fontCksum = NULL; 180 const UXMLElement *element; 181 int32_t ec = 0; 182 int32_t charCount = 0; 183 int32_t typoFlags = 3; // kerning + ligatures... 184 UScriptCode scriptCode; 185 le_int32 languageCode = -1; 186 UnicodeString text, glyphs, indices, positions; 187 int32_t glyphCount = 0, indexCount = 0, positionCount = 0; 188 TestResult expected = {0, NULL, NULL, NULL}; 189 190 uscript_getCode(script, &scriptCode, 1, &status); 191 if (LE_FAILURE(status)) { 192 log_err("invalid script name: %s.\n", script); 193 goto free_c_strings; 194 } 195 196 if (lang != NULL) { 197 languageCode = getLanguageCode(lang); 198 199 if (languageCode < 0) { 200 log_err("invalid language name: %s.\n", lang); 201 goto free_c_strings; 202 } 203 } 204 205 while((element = testCase->nextChildElement(ec)) != NULL) { 206 UnicodeString tag = element->getTagName(); 207 208 // TODO: make sure that each element is only used once. 209 if (tag.compare(test_font) == 0) { 210 fontName = getCString(element->getAttribute(name_attr)); 211 fontVer = getCString(element->getAttribute(ver_attr)); 212 fontCksum = getCString(element->getAttribute(cksum_attr)); 213 214 } else if (tag.compare(test_text) == 0) { 215 text = element->getText(TRUE); 216 charCount = text.length(); 217 } else if (tag.compare(result_glyphs) == 0) { 218 glyphs = element->getText(TRUE); 219 } else if (tag.compare(result_indices) == 0) { 220 indices = element->getText(TRUE); 221 } else if (tag.compare(result_positions) == 0) { 222 positions = element->getText(TRUE); 223 } else { 224 // an unknown tag... 225 char *cTag = getCString(&tag); 226 227 log_info("Test %s: unknown element with tag \"%s\"\n", id, cTag); 228 freeCString(cTag); 229 } 230 } 231 232 expected.glyphs = (LEGlyphID *) getHexArray(glyphs, glyphCount); 233 expected.indices = (le_int32 *) getHexArray(indices, indexCount); 234 expected.positions = getFloatArray(positions, positionCount); 235 236 expected.glyphCount = glyphCount; 237 238 if (glyphCount < charCount || indexCount != glyphCount || positionCount < glyphCount * 2 + 2) { 239 log_err("Test %s: inconsistent input data: charCount = %d, glyphCount = %d, indexCount = %d, positionCount = %d\n", 240 id, charCount, glyphCount, indexCount, positionCount); 241 goto free_expected; 242 }; 243 244 (*callback)(id, fontName, fontVer, fontCksum, scriptCode, languageCode, text.getBuffer(), charCount, &expected); 245 246 free_expected: 247 DELETE_ARRAY(expected.positions); 248 DELETE_ARRAY(expected.indices); 249 DELETE_ARRAY(expected.glyphs); 250 251 free_c_strings: 252 freeCString(fontCksum); 253 freeCString(fontVer); 254 freeCString(fontName); 255 freeCString(lang); 256 freeCString(script); 257 freeCString(id); 258 } 259 } 260 261 delete root; 262 delete parser; 263 #endif 264 } 265 U_CDECL_END 266