From 688810f14378db7bef3ae82741836f2faaff794f Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Mon, 13 Nov 2023 16:18:02 -0500 Subject: [PATCH] Save work. --- .gitignore | 1 + Makefile | 13 ++- pdfio-content.c | 4 +- testttf.c | 305 ++++++++++++++++++++++++++++++++++++++++++++++++ ttf.c | 24 ++-- 5 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 testttf.c diff --git a/.gitignore b/.gitignore index d8ab3ae..d9697a1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ /pdfiototext /testpdfio /testpdfio-*.pdf +/testttf /x64 diff --git a/Makefile b/Makefile index 24482d9..c2bd89a 100644 --- a/Makefile +++ b/Makefile @@ -67,12 +67,14 @@ LIBOBJS = \ OBJS = \ $(LIBOBJS) \ pdfiototext.o \ - testpdfio.o + testpdfio.o \ + testttf.o TARGETS = \ $(DSONAME) \ libpdfio.a \ pdfiototext \ - testpdfio + testpdfio \ + testttf # Make everything @@ -136,6 +138,7 @@ install-shared: # Test everything test: testpdfio + ./testttf 2>test.log ./testpdfio 2>test.log valgrind: testpdfio @@ -182,10 +185,16 @@ testpdfio: testpdfio.o libpdfio.a echo Linking $@... $(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS) +# TTF test program +testttf: ttf.o testttf.o + echo Linking $@... + $(CC) $(LDFLAGS) -o testttf ttf.o testttf.o $(LIBS) + # Dependencies $(OBJS): pdfio.h pdfio-private.h Makefile pdfio-content.o: pdfio-content.h ttf.h +testttf.o: ttf.h ttf.o: ttf.h # Make documentation using Codedoc diff --git a/pdfio-content.c b/pdfio-content.c index 8e2a94c..f88d265 100644 --- a/pdfio-content.c +++ b/pdfio-content.c @@ -1466,8 +1466,8 @@ pdfioFileCreateFontObjFromFile( else { // Map to specified glyph... - *bufptr++ = (unsigned char)((cmap[i] + 1) >> 8); - *bufptr++ = (unsigned char)((cmap[i] + 1) & 255); + *bufptr++ = (unsigned char)(cmap[i] >> 8); + *bufptr++ = (unsigned char)(cmap[i] & 255); } if (bufptr >= bufend) diff --git a/testttf.c b/testttf.c new file mode 100644 index 0000000..dc4b55f --- /dev/null +++ b/testttf.c @@ -0,0 +1,305 @@ +// +// Unit test program for TTF library +// +// https://github.com/michaelrsweet/ttf +// +// Copyright © 2018-2023 by Michael R Sweet. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// +// Usage: +// +// ./testttf [FILENAME] +// + +#include +#include "ttf.h" + + +// +// Local functions... +// + +static void error_cb(void *data, const char *message); +static int test_font(const char *filename); + + +// +// 'main()' - Main entry for unit tests. +// + +int // O - Exit status +main(int argc, // I - Number of command-line arguments + char *argv[]) // I - Command-line arguments +{ + int i; // Looping var + int errors = 0; // Number of errors + + + if (argc > 1) + { + for (i = 1; i < argc; i ++) + errors += test_font(argv[i]); + } + else + { + // Test with the bundled TrueType files... + errors += test_font("testfiles/OpenSans-Bold.ttf"); + errors += test_font("testfiles/OpenSans-Regular.ttf"); + errors += test_font("testfiles/NotoSansJP-Regular.otf"); + } + + if (!errors) + puts("\nALL TESTS PASSED"); + else + printf("\n%d TEST(S) FAILED\n", errors); + + return (errors); +} + + +// +// 'error_cb()' - Error callback. +// + +static void +error_cb(void *data, // I - User data (not used) + const char *message) // I - Message string +{ + fprintf(stderr, "FAIL (%s)\n", message); +} + + +// +// 'test_font()' - Test a font file. +// + +static int // O - Number of errors +test_font(const char *filename) // I - Font filename +{ + int i, // Looping var + errors = 0; // Number of errors + ttf_t *font; // Font + const char *value; // Font (string) value + int intvalue; // Font (integer) value + float realvalue; // Font (real) value + ttf_rect_t bounds; // Bounds + ttf_rect_t extents; // Extents + size_t num_fonts; // Number of fonts + ttf_style_t style; // Font style + ttf_weight_t weight; // Font weight + static const char * const stretches[] = + { // Font stretch strings + "TTF_STRETCH_NORMAL", // normal + "TTF_STRETCH_ULTRA_CONDENSED", // ultra-condensed + "TTF_STRETCH_EXTRA_CONDENSED", // extra-condensed + "TTF_STRETCH_CONDENSED", // condensed + "TTF_STRETCH_SEMI_CONDENSED", // semi-condensed + "TTF_STRETCH_SEMI_EXPANDED", // semi-expanded + "TTF_STRETCH_EXPANDED", // expanded + "TTF_STRETCH_EXTRA_EXPANDED", // extra-expanded + "TTF_STRETCH_ULTRA_EXPANDED" // ultra-expanded + }; + static const char * const strings[] = // Test strings + { + "Hello, World!", // English + "مرحبا بالعالم!", // Arabic + "Bonjour le monde!", // French + "Γειά σου Κόσμε!", // Greek + "שלום עולם!", // Hebrew + "Привет мир!", // Russian + "こんにちは世界!" // Japanese + }; + static const char * const styles[] = // Font style names + { + "TTF_STYLE_NORMAL", + "TTF_STYLE_ITALIC", + "TTF_STYLE_OBLIQUE" + }; + + + printf("ttfCreate(\"%s\"): ", filename); + if ((font = ttfCreate(filename, 0, error_cb, NULL)) != NULL) + puts("PASS"); + else + errors ++; + + fputs("ttfGetAscent: ", stdout); + if ((intvalue = ttfGetAscent(font)) > 0) + { + printf("PASS (%d)\n", intvalue); + } + else + { + printf("FAIL (%d)\n", intvalue); + errors ++; + } + + fputs("ttfGetBounds: ", stdout); + if (ttfGetBounds(font, &bounds)) + { + printf("PASS (%g %g %g %g)\n", bounds.left, bounds.bottom, bounds.right, bounds.top); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetCapHeight: ", stdout); + if ((intvalue = ttfGetCapHeight(font)) > 0) + { + printf("PASS (%d)\n", intvalue); + } + else + { + printf("FAIL (%d)\n", intvalue); + errors ++; + } + + fputs("ttfGetCopyright: ", stdout); + if ((value = ttfGetCopyright(font)) != NULL) + { + printf("PASS (%s)\n", value); + } + else + { + puts("FAIL"); + errors ++; + } + + for (i = 0; i < (int)(sizeof(strings) / sizeof(strings[0])); i ++) + { + printf("ttfGetExtents(\"%s\"): ", strings[i]); + if (ttfGetExtents(font, 12.0f, strings[i], &extents)) + { + printf("PASS (%.1f %.1f %.1f %.1f)\n", extents.left, extents.bottom, extents.right, extents.top); + } + else + { + puts("FAIL"); + errors ++; + } + } + + fputs("ttfGetFamily: ", stdout); + if ((value = ttfGetFamily(font)) != NULL) + { + printf("PASS (%s)\n", value); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetItalicAngle: ", stdout); + if ((realvalue = ttfGetItalicAngle(font)) >= 0.0) + { + printf("PASS (%g)\n", realvalue); + } + else + { + printf("FAIL (%g)\n", realvalue); + errors ++; + } + + fputs("ttfGetNumFonts: ", stdout); + if ((num_fonts = ttfGetNumFonts(font)) > 0) + { + printf("PASS (%u)\n", (unsigned)num_fonts); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetPostScriptName: ", stdout); + if ((value = ttfGetPostScriptName(font)) != NULL) + { + printf("PASS (%s)\n", value); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetStretch: ", stdout); + if ((intvalue = (int)ttfGetStretch(font)) >= TTF_STRETCH_NORMAL && intvalue <= TTF_STRETCH_ULTRA_EXPANDED) + { + printf("PASS (%s)\n", stretches[intvalue]); + } + else + { + printf("FAIL (%d)\n", intvalue); + errors ++; + } + + fputs("ttfGetStyle: ", stdout); + if ((style = ttfGetStyle(font)) >= TTF_STYLE_NORMAL && style <= TTF_STYLE_ITALIC) + { + printf("PASS (%s)\n", styles[style]); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetVersion: ", stdout); + if ((value = ttfGetVersion(font)) != NULL) + { + printf("PASS (%s)\n", value); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetWeight: ", stdout); + if ((weight = ttfGetWeight(font)) >= 0) + { + printf("PASS (%u)\n", (unsigned)weight); + } + else + { + puts("FAIL"); + errors ++; + } + + fputs("ttfGetWidth(' '): ", stdout); + if ((intvalue = ttfGetWidth(font, ' ')) > 0) + { + printf("PASS (%d)\n", intvalue); + } + else + { + printf("FAIL (%d)\n", intvalue); + errors ++; + } + + fputs("ttfGetXHeight: ", stdout); + if ((intvalue = ttfGetXHeight(font)) > 0) + { + printf("PASS (%d)\n", intvalue); + } + else + { + printf("FAIL (%d)\n", intvalue); + errors ++; + } + + fputs("ttfIsFixedPitch: ", stdout); + if (ttfIsFixedPitch(font)) + puts("PASS (true)"); + else + puts("PASS (false)"); + + ttfDelete(font); + + return (errors); +} diff --git a/ttf.c b/ttf.c index 5a3beb8..99c42ec 100644 --- a/ttf.c +++ b/ttf.c @@ -75,6 +75,7 @@ typedef __int64 ssize_t; // POSIX type not present on Windows... // DEBUG is defined and is a no-op otherwise... // +#define DEBUG #ifdef DEBUG # define TTF_DEBUG(...) fprintf(stderr, __VA_ARGS__) #else @@ -448,7 +449,7 @@ ttfCreate(const char *filename, // I - Filename font->cap_height = font->ascent; if (font->x_height == 0) - font->x_height = 3 * font->ascent / 5; + font->x_height = 3 * font->ascent / 5; // Build a sparse glyph widths table... font->min_char = -1; @@ -458,7 +459,8 @@ ttfCreate(const char *filename, // I - Filename if (font->cmap[i] >= 0) { int bin = (int)i / 256, // Sub-array bin - glyph = font->cmap[i]; // Glyph index + glyph = font->cmap[i] + 1; + // Glyph index (+1 to get past .notdef) // Update min/max... if (font->min_char < 0) @@ -687,9 +689,6 @@ ttfGetExtents( ch = *s++; } - // Issue #1: Offset past ".notdef"... - ch ++; - // Find its width... if ((widths = font->widths[ch / 256]) != NULL) { @@ -855,17 +854,14 @@ int // O - Width in 1000ths ttfGetWidth(ttf_t *font, // I - Font int ch) // I - Unicode character { - int bin; // Bin in widths array + ch --; + int bin = ch >> 8; // Bin in widths array // Range check input... if (!font || ch < ' ' || ch == 0x7f) return (0); - // Issue #1: Offset past ".notdef"... - ch ++; - bin = ch >> 8; - if (font->widths[bin]) return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units)); else if (font->widths[0]) // .notdef @@ -1309,17 +1305,17 @@ read_cmap(ttf_t *font) // I - Font { // Use an "obscure indexing trick" (words from the spec, not // mine) to look up the glyph index... - temp = segment->idRangeOffset / 2 + ch - segment->startCode + seg - segCount - 1; + temp = segment->idRangeOffset / 2 + ch - segment->startCode + seg - segCount; - if (temp < 0 || temp >= numGlyphIdArray || !glyphIdArray[temp]) + if (temp < 0 || temp >= numGlyphIdArray) glyph = -1; else - glyph = ((glyphIdArray[temp] + segment->idDelta) & 65535) - 1; + glyph = (glyphIdArray[temp] + segment->idDelta) & 65535; } else { // Just use idDelta to compute a glyph index... - glyph = ((ch + segment->idDelta) & 65535) - 1; + glyph = (ch + segment->idDelta) & 65535; } cmapptr[ch] = glyph;