]>
Commit | Line | Data |
---|---|---|
1 | // © 2016 and later: Unicode, Inc. and others. | |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
3 | /* | |
4 | ******************************************************************************* | |
5 | * | |
6 | * Copyright (C) 1999-2013, International Business Machines | |
7 | * Corporation and others. All Rights Reserved. | |
8 | * | |
9 | ******************************************************************************* | |
10 | * file name: gendata.cpp | |
11 | * | |
12 | * created on: 11/03/2000 | |
13 | * created by: Eric R. Mader | |
14 | */ | |
15 | ||
16 | #include <stdio.h> | |
17 | #include <string.h> | |
18 | #include <time.h> | |
19 | ||
20 | #include "unicode/utypes.h" | |
21 | #include "unicode/unistr.h" | |
22 | #include "unicode/uscript.h" | |
23 | #include "unicode/ubidi.h" | |
24 | #include "unicode/ustring.h" | |
25 | ||
26 | #include "layout/LETypes.h" | |
27 | #include "layout/LEScripts.h" | |
28 | #include "layout/LayoutEngine.h" | |
29 | ||
30 | #include "PortableFontInstance.h" | |
31 | #include "SimpleFontInstance.h" | |
32 | ||
33 | #include "xmlparser.h" | |
34 | ||
35 | #include "letsutil.h" | |
36 | #include "letest.h" | |
37 | ||
38 | U_NAMESPACE_USE | |
39 | ||
40 | static LEErrorCode overallStatus = LE_NO_ERROR; | |
41 | struct TestInput | |
42 | { | |
43 | const char *fontName; | |
44 | LEUnicode *text; | |
45 | le_int32 textLength; | |
46 | le_int32 scriptCode; | |
47 | le_bool rightToLeft; | |
48 | }; | |
49 | ||
50 | /* Returns the path to icu/source/test/testdata/ */ | |
51 | const char *getSourceTestData() { | |
52 | const char *srcDataDir = NULL; | |
53 | #ifdef U_TOPSRCDIR | |
54 | srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING; | |
55 | #else | |
56 | srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING; | |
57 | FILE *f = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"rbbitst.txt", "r"); | |
58 | ||
59 | if (f != NULL) { | |
60 | /* We're in icu/source/test/letest/ */ | |
61 | fclose(f); | |
62 | } else { | |
63 | /* We're in icu/source/test/letest/(Debug|Release) */ | |
64 | srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING; | |
65 | } | |
66 | #endif | |
67 | ||
68 | return srcDataDir; | |
69 | } | |
70 | ||
71 | const char *getPath(char buffer[2048], const char *filename) { | |
72 | const char *testDataDirectory = getSourceTestData(); | |
73 | ||
74 | strcpy(buffer, testDataDirectory); | |
75 | strcat(buffer, filename); | |
76 | ||
77 | return buffer; | |
78 | } | |
79 | ||
80 | /* | |
81 | * FIXME: should use the output file name and the current date. | |
82 | */ | |
83 | const char *header = | |
84 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
85 | "\n" | |
86 | "<!--\n" | |
87 | " Copyright (c) 1999-%4.4d International Business Machines\n" | |
88 | " Corporation and others. All rights reserved.\n" | |
89 | "\n" | |
90 | " WARNING: THIS FILE IS MACHINE GENERATED. DO NOT HAND EDIT IT\n" | |
91 | " UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.\n" | |
92 | "\n" | |
93 | " file name: letest.xml\n" | |
94 | " generated on: %s\n" | |
95 | " generated by: gendata.cpp\n" | |
96 | "-->\n" | |
97 | "\n" | |
98 | "<layout-tests>\n"; | |
99 | ||
100 | void dumpLongs(FILE *file, const char *tag, le_int32 *longs, le_int32 count) { | |
101 | char lineBuffer[8 * 12 + 2]; | |
102 | le_int32 bufp = 0; | |
103 | ||
104 | fprintf(file, " <%s>\n", tag); | |
105 | ||
106 | for (int i = 0; i < count; i += 1) { | |
107 | if (i % 8 == 0 && bufp != 0) { | |
108 | fprintf(file, " %s\n", lineBuffer); | |
109 | bufp = 0; | |
110 | } | |
111 | ||
112 | bufp += sprintf(&lineBuffer[bufp], "0x%8.8X, ", longs[i]); | |
113 | } | |
114 | ||
115 | if (bufp != 0) { | |
116 | lineBuffer[bufp - 2] = '\0'; | |
117 | fprintf(file, " %s\n", lineBuffer); | |
118 | } | |
119 | ||
120 | fprintf(file, " </%s>\n\n", tag); | |
121 | } | |
122 | ||
123 | void dumpFloats(FILE *file, const char *tag, float *floats, le_int32 count) { | |
124 | char lineBuffer[8 * 16 + 2]; | |
125 | le_int32 bufp = 0; | |
126 | ||
127 | fprintf(file, " <%s>\n", tag); | |
128 | ||
129 | for (int i = 0; i < count; i += 1) { | |
130 | if (i % 8 == 0 && bufp != 0) { | |
131 | fprintf(file, " %s\n", lineBuffer); | |
132 | bufp = 0; | |
133 | } | |
134 | ||
135 | bufp += sprintf(&lineBuffer[bufp], "%f, ", floats[i]); | |
136 | } | |
137 | ||
138 | if (bufp != 0) { | |
139 | lineBuffer[bufp - 2] = '\0'; | |
140 | fprintf(file, " %s\n", lineBuffer); | |
141 | } | |
142 | ||
143 | fprintf(file, " </%s>\n", tag); | |
144 | } | |
145 | ||
146 | int main(int argc, char *argv[]) | |
147 | { | |
148 | UErrorCode status = U_ZERO_ERROR; | |
149 | const char *gendataFile = "gendata.xml"; | |
150 | FILE *outputFile = fopen(argv[1], "w"); | |
151 | if(argc>2) { | |
152 | gendataFile = argv[2]; | |
153 | } | |
154 | time_t now = time(NULL); | |
155 | struct tm *local = localtime(&now); | |
156 | const char *tmFormat = "%m/%d/%Y %I:%M:%S %p %Z"; | |
157 | char tmString[64]; | |
158 | le_uint32 count = 0; | |
159 | strftime(tmString, 64, tmFormat, local); | |
160 | fprintf(outputFile, header, local->tm_year + 1900, tmString); | |
161 | ||
162 | UXMLParser *parser = UXMLParser::createParser(status); | |
163 | UXMLElement *root = parser->parseFile(gendataFile, status); | |
164 | ||
165 | if (root == NULL) { | |
166 | printf("Error: Could not open %s\n", gendataFile); | |
167 | delete parser; | |
168 | return -1; | |
169 | } else if(U_FAILURE(status)) { | |
170 | printf("Error reading %s: %s\n", gendataFile, u_errorName(status)); | |
171 | return -2; | |
172 | } else { | |
173 | printf("Reading %s\n", gendataFile); | |
174 | } | |
175 | ||
176 | UnicodeString test_case = UNICODE_STRING_SIMPLE("test-case"); | |
177 | UnicodeString test_text = UNICODE_STRING_SIMPLE("test-text"); | |
178 | UnicodeString test_font = UNICODE_STRING_SIMPLE("test-font"); | |
179 | ||
180 | // test-case attributes | |
181 | UnicodeString id_attr = UNICODE_STRING_SIMPLE("id"); | |
182 | UnicodeString script_attr = UNICODE_STRING_SIMPLE("script"); | |
183 | UnicodeString lang_attr = UNICODE_STRING_SIMPLE("lang"); | |
184 | ||
185 | // test-font attributes | |
186 | UnicodeString name_attr = UNICODE_STRING_SIMPLE("name"); | |
187 | ||
188 | const UXMLElement *testCase; | |
189 | int32_t tc = 0; | |
190 | ||
191 | while((testCase = root->nextChildElement(tc)) != NULL) { | |
192 | if (testCase->getTagName().compare(test_case) == 0) { | |
193 | char *id = getCString(testCase->getAttribute(id_attr)); | |
194 | char *script = getCString(testCase->getAttribute(script_attr)); | |
195 | char *lang = getCString(testCase->getAttribute(lang_attr)); | |
196 | ++count; | |
197 | printf("\n ID %s\n", id); | |
198 | LEFontInstance *font = NULL; | |
199 | const UXMLElement *element; | |
200 | int32_t ec = 0; | |
201 | int32_t charCount = 0; | |
202 | int32_t typoFlags = LayoutEngine::kTypoFlagKern | LayoutEngine::kTypoFlagLiga; // kerning + ligatures... | |
203 | UScriptCode scriptCode; | |
204 | le_int32 languageCode = -1; | |
205 | UnicodeString text; | |
206 | int32_t glyphCount = 0; | |
207 | LEErrorCode leStatus = LE_NO_ERROR; | |
208 | LayoutEngine *engine = NULL; | |
209 | LEGlyphID *glyphs = NULL; | |
210 | le_int32 *indices = NULL; | |
211 | float *positions = NULL; | |
212 | ||
213 | uscript_getCode(script, &scriptCode, 1, &status); | |
214 | if (LE_FAILURE(status)) { | |
215 | printf("Error: invalid script name: %s.\n", script); | |
216 | goto free_c_strings; | |
217 | } | |
218 | ||
219 | if (lang != NULL) { | |
220 | languageCode = getLanguageCode(lang); | |
221 | ||
222 | if (languageCode < 0) { | |
223 | printf("Error: invalid language name: %s.\n", lang); | |
224 | goto free_c_strings; | |
225 | } | |
226 | ||
227 | fprintf(outputFile, " <test-case id=\"%s\" script=\"%s\" lang=\"%s\">\n", id, script, lang); | |
228 | } else { | |
229 | fprintf(outputFile, " <test-case id=\"%s\" script=\"%s\">\n", id, script); | |
230 | } | |
231 | ||
232 | while((element = testCase->nextChildElement(ec)) != NULL) { | |
233 | UnicodeString tag = element->getTagName(); | |
234 | ||
235 | // TODO: make sure that each element is only used once. | |
236 | if (tag.compare(test_font) == 0) { | |
237 | char *fontName = getCString(element->getAttribute(name_attr)); | |
238 | const char *version = NULL; | |
239 | char buf[2048]; | |
240 | PortableFontInstance *pfi = new PortableFontInstance(getPath(buf,fontName), 12, leStatus); | |
241 | ||
242 | if (LE_FAILURE(leStatus)) { | |
243 | printf("Error: could not open font: %s (path: %s)\n", fontName, buf); | |
244 | freeCString(fontName); | |
245 | goto free_c_strings; | |
246 | } | |
247 | ||
248 | printf(" Generating: %s, %s, %s, %s\n", id, script, lang, fontName); | |
249 | ||
250 | version = pfi->getNameString(NAME_VERSION_STRING, PLATFORM_MACINTOSH, MACINTOSH_ROMAN, MACINTOSH_ENGLISH); | |
251 | ||
252 | // The standard recommends that the Macintosh Roman/English name string be present, but | |
253 | // if it's not, try the Microsoft Unicode/English string. | |
254 | if (version == NULL) { | |
255 | const LEUnicode16 *uversion = pfi->getUnicodeNameString(NAME_VERSION_STRING, PLATFORM_MICROSOFT, MICROSOFT_UNICODE_BMP, MICROSOFT_ENGLISH); | |
256 | ||
257 | if (uversion != NULL) { | |
258 | char uversion_utf8[300]; | |
259 | UErrorCode status2 = U_ZERO_ERROR; | |
260 | u_strToUTF8(uversion_utf8, 300, NULL, uversion, -1, &status2); | |
261 | if(U_FAILURE(status2)) { | |
262 | uversion_utf8[0]=0; | |
263 | } | |
264 | fprintf(outputFile, " <test-font name=\"%s\" version=\"%s\" checksum=\"0x%8.8X\" rchecksum=\"0x%8.8X\"/>\n\n", | |
265 | fontName, uversion_utf8, pfi->getFontChecksum(), pfi->getRawChecksum()); | |
266 | ||
267 | pfi->deleteNameString(uversion); | |
268 | } else { | |
269 | fprintf(outputFile, " <test-font name=\"%s\" version=\"unknown-0x%8.8X\" checksum=\"0x%8.8X\" rchecksum=\"0x%8.8X\"/>\n\n", | |
270 | fontName, pfi->getFontChecksum(), pfi->getFontChecksum(), pfi->getRawChecksum()); | |
271 | } | |
272 | } else { | |
273 | fprintf(outputFile, " <test-font name=\"%s\" version=\"%s\" checksum=\"0x%8.8X\" rchecksum=\"0x%8.8X\"/>\n\n", | |
274 | fontName, version, pfi->getFontChecksum(), pfi->getRawChecksum()); | |
275 | ||
276 | pfi->deleteNameString(version); | |
277 | } | |
278 | fflush(outputFile); | |
279 | ||
280 | freeCString(fontName); | |
281 | ||
282 | font = pfi; | |
283 | } else if (tag.compare(test_text) == 0) { | |
284 | char *utf8 = NULL; | |
285 | ||
286 | text = element->getText(TRUE); | |
287 | charCount = text.length(); | |
288 | ||
289 | utf8 = getUTF8String(&text); | |
290 | fprintf(outputFile, " <test-text>%s</test-text>\n\n", utf8); | |
291 | fflush(outputFile); | |
292 | freeCString(utf8); | |
293 | } else { | |
294 | // an unknown tag... | |
295 | char *cTag = getCString(&tag); | |
296 | ||
297 | printf("Test %s: unknown element with tag \"%s\"\n", id, cTag); | |
298 | freeCString(cTag); | |
299 | } | |
300 | } | |
301 | ||
302 | if (font == NULL) { | |
303 | LEErrorCode fontStatus = LE_NO_ERROR; | |
304 | ||
305 | font = new SimpleFontInstance(12, fontStatus); | |
306 | typoFlags |= 0x80000000L; // use CharSubstitutionFilter... | |
307 | } | |
308 | ||
309 | engine = LayoutEngine::layoutEngineFactory(font, scriptCode, languageCode, typoFlags, leStatus); | |
310 | ||
311 | if (LE_FAILURE(leStatus)) { | |
312 | printf("Error for test %s: could not create a LayoutEngine.\n", id); | |
313 | goto delete_font; | |
314 | } | |
315 | ||
316 | glyphCount = engine->layoutChars(text.getBuffer(), 0, charCount, charCount, getRTL(text), 0, 0, leStatus); | |
317 | ||
318 | glyphs = NEW_ARRAY(LEGlyphID, glyphCount); | |
319 | indices = NEW_ARRAY(le_int32, glyphCount); | |
320 | positions = NEW_ARRAY(float, glyphCount * 2 + 2); | |
321 | ||
322 | engine->getGlyphs(glyphs, leStatus); | |
323 | engine->getCharIndices(indices, leStatus); | |
324 | engine->getGlyphPositions(positions, leStatus); | |
325 | ||
326 | if(LE_FAILURE(leStatus)) { | |
327 | fprintf(stderr,"ERROR: LO returned error: %s\n", u_errorName((UErrorCode)leStatus)); | |
328 | overallStatus = leStatus; | |
329 | fprintf(outputFile, "<!-- ERROR: %d -->\n", leStatus); | |
330 | fflush(outputFile); | |
331 | leStatus = LE_NO_ERROR; | |
332 | } else { | |
333 | dumpLongs(outputFile, "result-glyphs", (le_int32 *) glyphs, glyphCount); | |
334 | ||
335 | dumpLongs(outputFile, "result-indices", indices, glyphCount); | |
336 | ||
337 | dumpFloats(outputFile, "result-positions", positions, glyphCount * 2 + 2); | |
338 | fflush(outputFile); | |
339 | ||
340 | } | |
341 | ||
342 | DELETE_ARRAY(positions); | |
343 | DELETE_ARRAY(indices); | |
344 | DELETE_ARRAY(glyphs); | |
345 | ||
346 | delete engine; | |
347 | ||
348 | delete_font: | |
349 | fprintf(outputFile, " </test-case>\n\n"); | |
350 | fflush(outputFile); | |
351 | ||
352 | delete font; | |
353 | ||
354 | free_c_strings: | |
355 | freeCString(lang); | |
356 | freeCString(script); | |
357 | freeCString(id); | |
358 | } | |
359 | } | |
360 | ||
361 | delete root; | |
362 | delete parser; | |
363 | ||
364 | fprintf(outputFile, "</layout-tests>\n"); | |
365 | ||
366 | if(count==0) { | |
367 | fprintf(stderr, "No cases processed!\n"); | |
368 | return 1; | |
369 | } | |
370 | ||
371 | ||
372 | if(LE_FAILURE(overallStatus)) { | |
373 | fprintf(outputFile, "<!-- !!! FAILED. %d -->\n", overallStatus); | |
374 | fprintf(stderr, "!!! FAILED. %d\n", overallStatus); | |
375 | fclose(outputFile); | |
376 | return 0; | |
377 | // return 1; | |
378 | } else { | |
379 | printf("Generated.\n"); | |
380 | fclose(outputFile); | |
381 | return 0; | |
382 | } | |
383 | } |