HarfBuzz text shaping engine
http://harfbuzz.github.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
12 KiB
437 lines
12 KiB
/*____________________________________________________________________________ |
|
|
|
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); |
|
hb_ot_font_set_funcs (font); |
|
|
|
// 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] != -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); |
|
} |
|
|
|
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+"); |
|
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, "\" --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+"); |
|
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, "\" --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+"); |
|
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, "\" --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); |
|
} |
|
|
|
|
|
|
|
|