| /*____________________________________________________________________________ | |
| Copyright 2000-2016 Adobe Systems Incorporated. All Rights Reserved. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use these files except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| ____________________________________________________________________________*/ | |
| #include "stdlib.h" | |
| #include "stdio.h" | |
| #include "string.h" | |
| #include "hb.h" | |
| #include "hb-ot.h" | |
| static const bool verbose = true; | |
| struct TestData | |
| { | |
| TestData(hb_buffer_t *buffer_, | |
| hb_face_t *face_, | |
| hb_font_t *font_, | |
| hb_feature_t *features_, | |
| int num_features_) | |
| : buffer(buffer_), face(face_), font(font_), | |
| features(features_), num_features(num_features_) | |
| { } | |
| ~TestData() | |
| { | |
| free (features); | |
| hb_face_destroy (face); | |
| hb_font_destroy (font); | |
| hb_buffer_destroy (buffer); | |
| } | |
| hb_buffer_t *buffer; | |
| hb_face_t *face; | |
| hb_font_t *font; | |
| hb_feature_t *features; | |
| int num_features; | |
| }; | |
| TestData | |
| runTest(const char *testName, | |
| const char *fontfileName, | |
| unsigned int *in, int nbIn, | |
| unsigned int *select, int nbSelect) | |
| { | |
| FILE *f = fopen (fontfileName, "rb"); | |
| fseek(f, 0, SEEK_END); | |
| long fontsize = ftell(f); | |
| fseek(f, 0, SEEK_SET); | |
| char *fontdata = (char *)malloc (fontsize); | |
| fread(fontdata, fontsize, 1, f); | |
| fclose(f); | |
| if (verbose) { | |
| printf ("------------------------------- %s\n", testName); | |
| } | |
| // setup font | |
| hb_blob_t *blob = hb_blob_create(fontdata, fontsize, | |
| HB_MEMORY_MODE_WRITABLE, | |
| 0, 0); | |
| hb_face_t *face = hb_face_create(blob, 0); | |
| hb_font_t *font = hb_font_create(face); | |
| unsigned int upem = hb_face_get_upem (face); | |
| hb_font_set_scale(font, upem, upem); | |
| // setup buffer | |
| hb_buffer_t *buffer = hb_buffer_create(); | |
| hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
| hb_buffer_set_script(buffer, HB_SCRIPT_LATIN); | |
| hb_buffer_set_language(buffer, hb_language_from_string("en", 2)); | |
| hb_buffer_add_utf32(buffer, in, nbIn, 0, nbIn); | |
| // setup features | |
| hb_feature_t *features; | |
| int nbFeatures; | |
| if (nbSelect == 0) | |
| { | |
| nbFeatures = 1; | |
| features = (hb_feature_t *) malloc (sizeof (*features)); | |
| features[0].tag = HB_TAG('t', 'e', 's', 't'); | |
| features[0].value = 1; | |
| features[0].start = HB_FEATURE_GLOBAL_START; | |
| features[0].end = HB_FEATURE_GLOBAL_END; | |
| } | |
| else | |
| { | |
| nbFeatures = 0; | |
| features = (hb_feature_t *) malloc (sizeof (*features) * nbSelect); | |
| for (int i = 0; i < nbSelect; i++) { | |
| if (select[i] != (unsigned) -1) { | |
| features[nbFeatures].tag = HB_TAG('t', 'e', 's', 't'); | |
| features[nbFeatures].value = select[i]; | |
| features[nbFeatures].start = i; | |
| features[nbFeatures].end = i + 1; | |
| nbFeatures++; | |
| } | |
| } | |
| } | |
| // shape | |
| hb_shape(font, buffer, features, nbFeatures); | |
| hb_blob_destroy(blob); | |
| return TestData(buffer, face, font, features, nbFeatures); | |
| } | |
| void printArray (const char* s, int *a, int n) | |
| { | |
| printf ("%s %d : ", s, n); | |
| for (int i = 0; i < n; i++) { | |
| printf (" %d", a[i]); | |
| } | |
| printf ("\n"); | |
| } | |
| void printUArray (const char* s, unsigned int *a, int n) | |
| { | |
| printArray (s, (int *) a, n); | |
| } | |
| void writeDirective (FILE *file) | |
| { | |
| fseek (file, 0, SEEK_END); | |
| unsigned file_size = ftell (file); | |
| if (!file_size) | |
| fprintf (file, "@shaper=ot\n"); | |
| } | |
| bool gsub_test(const char *testName, | |
| const char *fontfileName, | |
| int nbIn, unsigned int *in, | |
| int nbSelect, unsigned int *select, | |
| int nbExpected, unsigned int *expected) | |
| { | |
| TestData data = runTest(testName, | |
| fontfileName, | |
| in, nbIn, | |
| select, nbSelect); | |
| // verify | |
| hb_glyph_info_t *actual = hb_buffer_get_glyph_infos(data.buffer, 0); | |
| unsigned int nbActual = hb_buffer_get_length(data.buffer); | |
| bool ok = true; | |
| if (nbActual != nbExpected) | |
| ok = false; | |
| else { | |
| for (int i = 0; i < nbActual; i++) { | |
| if (actual[i].codepoint != expected [i]) { | |
| ok = false; | |
| break; | |
| } | |
| } | |
| } | |
| char test_name[255]; | |
| sprintf (test_name, "../../tests/%.*s.tests", (int) (strrchr (testName, '_') - testName), testName); | |
| FILE *tests_file = fopen (test_name, "a+"); | |
| writeDirective (tests_file); | |
| if (!ok) fprintf (tests_file, "#"); | |
| fprintf (tests_file, "../fonts/%s;--features=\"", fontfileName + 9); | |
| for (unsigned int i = 0; i < data.num_features; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| char buf[255]; | |
| hb_feature_to_string (&data.features[i], buf, sizeof (buf)); | |
| fprintf (tests_file, "%s", buf); | |
| } | |
| fprintf (tests_file, "\" --single-par --no-clusters --no-glyph-names --no-positions;"); | |
| for (unsigned int i = 0; i < nbIn; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| fprintf (tests_file, "U+%04X", in[i]); | |
| } | |
| fprintf (tests_file, ";["); | |
| for (unsigned int i = 0; i < nbActual; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, "|"); | |
| fprintf (tests_file, "%d", expected[i]); | |
| } | |
| fprintf (tests_file, "]"); | |
| fprintf (tests_file, "\n"); | |
| fclose (tests_file); | |
| if (! ok) { | |
| printf ("******* GSUB %s\n", testName); | |
| printf ("expected %d:", nbExpected); | |
| for (int i = 0; i < nbExpected; i++) { | |
| printf (" %d", expected[i]); } | |
| printf ("\n"); | |
| printf (" actual %d:", nbActual); | |
| for (int i = 0; i < nbActual; i++) { | |
| printf (" %d", actual[i].codepoint); } | |
| printf ("\n"); | |
| } | |
| return ok; | |
| } | |
| bool cmap_test(const char *testName, | |
| const char *fontfileName, | |
| int nbIn, unsigned int *in, | |
| int nbSelect, unsigned int *select, | |
| int nbExpected, unsigned int *expected) | |
| { | |
| TestData data = runTest(testName, | |
| fontfileName, | |
| in, nbIn, | |
| select, nbSelect); | |
| // verify | |
| hb_glyph_info_t *actual = hb_buffer_get_glyph_infos(data.buffer, 0); | |
| unsigned int nbActual = hb_buffer_get_length(data.buffer); | |
| bool ok = true; | |
| if (nbActual != nbExpected) | |
| ok = false; | |
| else { | |
| for (int i = 0; i < nbActual; i++) { | |
| if (actual[i].codepoint != expected [i]) { | |
| ok = false; | |
| break; | |
| } | |
| } | |
| } | |
| char test_name[255]; | |
| sprintf (test_name, "../../tests/%.*s.tests", (int) (strrchr (testName, '_') - testName), testName); | |
| FILE *tests_file = fopen (test_name, "a+"); | |
| writeDirective (tests_file); | |
| if (!ok) fprintf (tests_file, "#"); | |
| fprintf (tests_file, "../fonts/%s;--features=\"", fontfileName + 9); | |
| for (unsigned int i = 0; i < data.num_features; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| char buf[255]; | |
| hb_feature_to_string (&data.features[i], buf, sizeof (buf)); | |
| fprintf (tests_file, "%s", buf); | |
| } | |
| fprintf (tests_file, "\" --single-par --no-clusters --no-glyph-names --no-positions --font-funcs=ot;"); | |
| for (unsigned int i = 0; i < nbIn; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| fprintf (tests_file, "U+%04X", in[i]); | |
| } | |
| fprintf (tests_file, ";["); | |
| for (unsigned int i = 0; i < nbActual; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, "|"); | |
| fprintf (tests_file, "%d", expected[i]); | |
| } | |
| fprintf (tests_file, "]"); | |
| fprintf (tests_file, "\n"); | |
| fclose (tests_file); | |
| if (! ok) { | |
| printf ("******* cmap %s\n", testName); | |
| printf ("expected %d:", nbExpected); | |
| for (int i = 0; i < nbExpected; i++) { | |
| printf (" %d", expected[i]); } | |
| printf ("\n"); | |
| printf (" actual %d:", nbActual); | |
| for (int i = 0; i < nbActual; i++) { | |
| printf (" %d", actual[i].codepoint); } | |
| printf ("\n"); | |
| } | |
| return ok; | |
| } | |
| bool gpos_test(const char *testName, | |
| const char *fontfileName, | |
| int nbIn, | |
| unsigned int *in, | |
| int nbOut, | |
| unsigned int *out, | |
| int *x, | |
| int *y) | |
| { | |
| TestData data = runTest(testName, | |
| fontfileName, | |
| in, nbIn, | |
| 0, 0); | |
| // verify | |
| unsigned int nbActual; | |
| hb_glyph_info_t *actual = hb_buffer_get_glyph_infos(data.buffer, &nbActual); | |
| hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (data.buffer, NULL); | |
| unsigned int *actualG = (unsigned int *) malloc(sizeof(*actualG) * nbActual); | |
| int *actualX = (int *) malloc(sizeof(*actualX) * nbActual); | |
| int *actualY = (int *) malloc(sizeof(*actualY) * nbActual); | |
| int curX = 0; | |
| int curY = 0; | |
| for (int i = 0; i < nbActual; i++) { | |
| actualG[i] = actual[i].codepoint; | |
| actualX[i] = curX + pos[i].x_offset; | |
| actualY[i] = curY + pos[i].y_offset; | |
| curX += pos[i].x_advance; | |
| if (hb_ot_layout_get_glyph_class (data.face, actualG[i]) != HB_OT_LAYOUT_GLYPH_CLASS_MARK) | |
| curX -= 1500; | |
| curY += pos[i].y_advance; | |
| } | |
| bool nbOk = true; | |
| bool xOk = true; | |
| bool yOk = true; | |
| if (nbActual != nbOut) | |
| nbOk = false; | |
| else { | |
| for (int i = 0; i < nbActual; i++) { | |
| if (actualX[i] != x[i]) { | |
| xOk = false; | |
| } | |
| if (actualY[i] != y[i]) { | |
| yOk = false; | |
| } | |
| } | |
| } | |
| bool ok = (nbOk && xOk && yOk); | |
| if (! ok) { | |
| printf ("******* GPOS %s\n", testName); | |
| if (! (nbOk && xOk)) { | |
| printArray ("expectedX", x, nbOut); | |
| printArray ("actualX ", actualX, nbActual); | |
| printf ("xadv/pos:"); | |
| for (int i = 0; i < nbOut; i++) { | |
| printf (" %d/%d", pos[i].x_advance, pos[i].x_offset); | |
| } | |
| printf ("\n"); | |
| } | |
| if (! (nbOk && yOk)) { | |
| printArray ("expectedY", y, nbOut); | |
| printArray ("actualY ", actualY, nbActual); | |
| printf ("yadv/pos:"); | |
| for (int i = 0; i < nbOut; i++) { | |
| printf (" %d/%d", pos[i].y_advance, pos[i].y_offset); | |
| } | |
| printf ("\n"); | |
| } | |
| } | |
| char test_name[255]; | |
| sprintf (test_name, "../../tests/%.*s.tests", (int) (strrchr (testName, '_') - testName), testName); | |
| FILE *tests_file = fopen (test_name, "a+"); | |
| writeDirective (tests_file); | |
| if (!ok) fprintf (tests_file, "#"); | |
| fprintf (tests_file, "../fonts/%s;--features=\"", fontfileName + 9); | |
| for (unsigned int i = 0; i < data.num_features; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| char buf[255]; | |
| hb_feature_to_string (&data.features[i], buf, sizeof (buf)); | |
| fprintf (tests_file, "%s", buf); | |
| } | |
| fprintf (tests_file, "\" --single-par --no-clusters --no-glyph-names --ned;"); | |
| for (unsigned int i = 0; i < nbIn; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, ","); | |
| fprintf (tests_file, "U+%04X", in[i]); | |
| } | |
| fprintf (tests_file, ";["); | |
| int accumlatedAdvance = 0; | |
| for (unsigned int i = 0; i < nbActual; i++) | |
| { | |
| if (i != 0) fprintf (tests_file, "|"); | |
| fprintf (tests_file, "%d", /*it should be "out[i]"*/ actualG[i]); | |
| int expected_x = x[i] + accumlatedAdvance; | |
| int expected_y = y[i]; | |
| if (expected_x || expected_y) fprintf (tests_file, "@%d,%d", expected_x, expected_y); | |
| if (hb_ot_layout_get_glyph_class (data.face, actualG[i]) != HB_OT_LAYOUT_GLYPH_CLASS_MARK) | |
| accumlatedAdvance += 1500; | |
| } | |
| fprintf (tests_file, "]"); | |
| fprintf (tests_file, "\n"); | |
| fclose (tests_file); | |
| free(actualG); | |
| free(actualX); | |
| free(actualY); | |
| return ok; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| int failures = 0; | |
| int pass = 0; | |
| #include "hb-aots-tester.h" | |
| printf ("%d failures, %d pass\n", failures, pass); | |
| } | |