From d257d186ae2a08042a412824678f98241a1a4f3c Mon Sep 17 00:00:00 2001 From: "yusukes@chromium.org" Date: Wed, 4 Nov 2009 04:56:32 +0000 Subject: initial commit. git-svn-id: http://ots.googlecode.com/svn/trunk@2 a4e77c2c-9104-11de-800e-5b313e0d2bf3 --- test/README | 243 +++++++++++++++++++++++++++++++++++++++ test/SConstruct | 43 +++++++ test/file-stream.h | 39 +++++++ test/idempotent.cc | 94 ++++++++++++++++ test/ot-sanitise.cc | 54 +++++++++ test/perf.cc | 79 +++++++++++++ test/side-by-side.cc | 281 ++++++++++++++++++++++++++++++++++++++++++++++ test/validator-checker.cc | 117 +++++++++++++++++++ 8 files changed, 950 insertions(+) create mode 100644 test/README create mode 100644 test/SConstruct create mode 100644 test/file-stream.h create mode 100644 test/idempotent.cc create mode 100644 test/ot-sanitise.cc create mode 100644 test/perf.cc create mode 100644 test/side-by-side.cc create mode 100644 test/validator-checker.cc (limited to 'test') diff --git a/test/README b/test/README new file mode 100644 index 0000000..c31f7ce --- /dev/null +++ b/test/README @@ -0,0 +1,243 @@ +------------------------------------------------------------------------------ +ot-sanitise - TTF/OTF font transcoder + +Description: + ot-sanitise is a program which validates and transcodes a truetype or + opentype font file using the OTS library: + + transcoded_font = ValidateAndTranscode(original_font); + if (validation_error) + PrintErrorAndExit; + OutputToStdout(transcoded_font); + +Usage: + $ ./ot-sanitise ttf_or_otf_file > transcoded_file + +Example: + $ ./ot-sanitise sample.otf > transcoded_sample.otf + $ ./ot-sanitise malformed.ttf > transcoded_malformed.ttf + WARNING at ots/src/ots.cc:158: bad range shift + ERROR at ots/src/ots.cc:199 (bool::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t)) + Failed to sanitise file! + $ + +------------------------------------------------------------------------------ +idempotent - TTF/OTF font transcoder (for OTS debugging) + +Description: + idempotent is a program which validates and transcodes a truetype or opentype + font file using OTS. This tool transcodes the original font twice and then + verifies that the two transcoded fonts are identical: + + t1 = ValidateAndTranscode(original_font); + if (validation_error) + PrintErrorAndExit; + t2 = ValidateAndTranscode(t1); + if (validation_error) + PrintErrorAndExit; + if (t1 != t2) + PrintErrorAndExit; + + This tool is basically for OTS developers. + +Usage: + $ ./idempotent ttf_or_otf_file + +Example: + $ ./idempotent sample.otf + $ ./idempotent malformed.ttf + WARNING at ots/src/ots.cc:158: bad range shift + ERROR at ots/src/ots.cc:199 (bool::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t)) + Failed to sanitise file! + $ + +------------------------------------------------------------------------------ +validator_checker - font validation checker + +Description: + validator_checker is a program which is intended to validate malformed fonts. + If the program detects that the font is invalid, it prints "OK" and returns + with 0 (success). If it coulndn't detect any errors, the program then opens + the transcoded font and renders some characters using FreeType2: + + transcoded_font = ValidateAndTranscode(malicious_font); + if (validation_error) + Print("OK"); + OpenAndRenderSomeCharacters(transcoded_font); # may cause SIGSEGV + Print("OK"); + + If SEGV doesn't raise inside FreeType2 library, the program prints "OK" and + returns with 0 as well. You should run this tool under the catchsegv or + valgrind command so that you can easily verify that all transformed fonts + don't crash the library (see the example below). + +Usage: + $ catchsegv ./validator_checker malicous_ttf_or_otf_file + +Example: + $ for f in malformed/*.ttf ; do catchsegv ./validator-checker "$f" ; done + OK: the malicious font was filtered: malformed/1.ttf + OK: the malicious font was filtered: malformed/2.ttf + OK: FreeType2 didn't crash: malformed/3.ttf + OK: the malicious font was filtered: malformed/4.ttf + $ + +------------------------------------------------------------------------------ +perf - performance checker + +Description: + perf is a program which validates and transcodes a truetype or opentype font + file N times using OTS, then prints the elapsed time: + + for (N times) + ValidateAndTranscode(original_font); + Print(elapsed_time_in_us / N); + +Usage: + $ ./perf ttf_or_otf_file + +Example: + $ ./perf sample.ttf + 903 [us] sample.ttf (139332 bytes, 154 [byte/us]) + $ ./perf sample-bold.otf + 291 [us] sample-bold.otf (150652 bytes, 517 [byte/us]) + +------------------------------------------------------------------------------ +side-by-side - font quality checker + +Description: + side-by-side is a program which renders some characters (ASCII, Latin-1, CJK) + using both original font and transcoded font and checks that the two rendering + results are exactly equal. + + The following Unicode characters are used during the test: + 0x0020 - 0x007E // Basic Latin + 0x00A1 - 0x017F // Latin-1 + 0x1100 - 0x11FF // Hangul + 0x3040 - 0x309F // Japanese HIRAGANA letters + 0x3130 - 0x318F // Hangul + 0x4E00 - 0x4F00 // CJK Kanji/Hanja + 0xAC00 - 0xAD00 // Hangul + + This tool uses FreeType2 library. + Note: This tool doesn't check kerning (GPOS/kern) nor font substitution + (GSUB). These should be tested in Layout tests if necessary. + +Usage: + $ ./side-by-side ttf_or_otf_file + +Example: + $ ./side-by-side linux/kochi-gothic.ttf # no problem + $ ./side-by-side free/kredit1.ttf # this is known issue of OTS. + bitmap metrics doesn't match! (14, 57), (37, 45) + EXPECTED: + + +#######*. + +##########+ + .###+.#. .#. + *#* # #* + ##. # ## + ## # ## + ## # ## + ## #. ## + ##. #. .## + ##. #. .## + *#+ *+ +#* + *#+ *+ +#* + *#+ *+ +#* + *#+ *+ +#* + *#+ *+ *#* + *#+ ++ *#+ + +#* +* *#+ + +#* +* *#+ + +#* +* *#+ + +#* +* ##. + +#* +* ##. + .## .# ## + .## .# ## + .## .# ## + ## # ## + ## # ## + ## # .## + ## # .## + ## .#+ +#* + ## +######* + ##.+#######* + *##########* + +##########+ + #########* + .######## + +####+ + + + + + + + .*######* + +##*.*##### + .##+.#+ +# + *#* ## #+ + ##*### ## + ###### ## + ##+.##+ +## + ## ########## + ## +######### + ## +######## + *#. .########* + .#* #########. + +##########+ + +*######* + + ACTUAL: + + .*##*+ + +##+.##*. + .#* .##.+#* + *# ### *#+ + #*######+ .*#+ + #########*. +#*. + ###########* +#* + *############+ *#+ + +##############. .##. + *##############* +#* + +###############+ *#+ + *###############+ .*#+ + .###############*. +#*. + +###############* +#* + *###############+ *#+ + .*###############+ .*#+ + +###############*. +#* + +###############* ** + *###############+ #+ + .###############* ## + +############+ ## + +########* .## + .######. +### + +#####+ .*#..# + +#####* *###..# + *#####. +#######* + +#####+ .*########. + +#####* +#########* + *#####. +##########+ + +#####+ *#########*. + .#####* +##########+ + *#####. +##########* + +#####+ *#########*. + .#####* +##########+ + *#####+ +##########* + .#*++#+ *#########*. + .#+ ## +##########+ + ****###+.##########* + ##################. + ###+ *#########+ + ## +########* + *#+ *########. + ##.#######+ + +#######* + *###*. + + + Glyph mismatch! (file: free/kredit1.ttf, U+0021, 100pt)! + $ +------------------------------------------------------------------------------ diff --git a/test/SConstruct b/test/SConstruct new file mode 100644 index 0000000..4e688d3 --- /dev/null +++ b/test/SConstruct @@ -0,0 +1,43 @@ +# Build script for Linux +# +# Usage: +# $ cd ots/test/ +# $ scons -c # clean +# $ scons # build +# + +# Since the validator-checker tool might handle malicious font files, all hardening options for recent g++/ld are enabled just in case. +# See http://wiki.debian.org/Hardening for details. +env = Environment(CCFLAGS = ['-O2', '-I../include', '-I/usr/include/freetype2', '-ggdb', '-Wall', '-W', '-Wno-unused-parameter', '-fPIE', '-fstack-protector', '-D_FORTIFY_SOURCE=2', '-DOTS_DEBUG'], LINKFLAGS = ['-ggdb', '-Wl,-z,relro', '-Wl,-z,now', '-pie']) +# TODO(yusukes): better to use pkg-config freetype2 --cflags + +env.Library('../src/libots.a', + ['../src/cff.cc', + '../src/cmap.cc', + '../src/cvt.cc', + '../src/fpgm.cc', + '../src/gasp.cc', + '../src/glyf.cc', + '../src/hdmx.cc', + '../src/head.cc', + '../src/hhea.cc', + '../src/hmtx.cc', + '../src/loca.cc', + '../src/ltsh.cc', + '../src/maxp.cc', + '../src/name.cc', + '../src/os2.cc', + '../src/ots.cc', + '../src/post.cc', + '../src/prep.cc', + '../src/vdmx.cc', + '../src/vorg.cc' + ]) + +env.Program('../test/ot-sanitise.cc', LIBS = ['ots'], LIBPATH='../src') +env.Program('../test/idempotent.cc', LIBS = ['ots'], LIBPATH='../src') +env.Program('../test/perf.cc', LIBS = ['ots'], LIBPATH='../src') + +# TODO(yusukes): better to use pkg-config freetype2 --libs +env.Program('../test/side-by-side.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH = '../src') +env.Program('../test/validator-checker.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH = '../src') diff --git a/test/file-stream.h b/test/file-stream.h new file mode 100644 index 0000000..58f85e1 --- /dev/null +++ b/test/file-stream.h @@ -0,0 +1,39 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OTS_FILE_STREAM_H_ +#define OTS_FILE_STREAM_H_ + +#include "opentype-sanitiser.h" + +namespace ots { + +class FILEStream : public OTSStream { + public: + explicit FILEStream(FILE *stream) + : file_(stream) { + } + + ~FILEStream() { + } + + bool WriteRaw(const void *data, size_t length) { + return fwrite(data, length, 1, file_) == 1; + } + + bool Seek(off_t position) { + return fseek(file_, position, SEEK_SET) == 0; + } + + off_t Tell() const { + return ftell(file_); + } + + private: + FILE * const file_; +}; + +} // namespace ots + +#endif // OTS_FILE_STREAM_H_ diff --git a/test/idempotent.cc b/test/idempotent.cc new file mode 100644 index 0000000..7f33a47 --- /dev/null +++ b/test/idempotent.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include +#include +#include + +#include "opentype-sanitiser.h" +#include "ots-memory-stream.h" + +namespace { + +int Usage(const char *argv0) { + std::fprintf(stderr, "Usage: %s \n", argv0); + return 1; +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) return Usage(argv[0]); + + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + + uint8_t *data = new uint8_t[st.st_size]; + if (::read(fd, data, st.st_size) != st.st_size) { + ::close(fd); + std::fprintf(stderr, "Failed to read file!\n"); + return 1; + } + ::close(fd); + + // A transcoded font is usually smaller than an original font. + // However, it can be slightly bigger than the original one due to + // name table replacement and/or padding for glyf table. + static const size_t kPadLen = 20 * 1024; + uint8_t *result = new uint8_t[st.st_size + kPadLen]; + ots::MemoryStream output(result, st.st_size + kPadLen); + + bool r = ots::Process(&output, data, st.st_size); + if (!r) { + std::fprintf(stderr, "Failed to sanitise file!\n"); + return 1; + } + const size_t result_len = output.Tell(); + free(data); + + uint8_t *result2 = new uint8_t[result_len]; + ots::MemoryStream output2(result2, result_len); + r = ots::Process(&output2, result, result_len); + if (!r) { + std::fprintf(stderr, "Failed to sanitise previous output!\n"); + return 1; + } + const size_t result2_len = output2.Tell(); + + bool dump_results = false; + if (result2_len != result_len) { + std::fprintf(stderr, "Outputs differ in length\n"); + dump_results = true; + } else if (std::memcmp(result2, result, result_len)) { + std::fprintf(stderr, "Outputs differ in content\n"); + dump_results = true; + } + + if (dump_results) { + std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n"); + int fd1 = ::open("out1.ttf", O_WRONLY | O_CREAT | O_TRUNC, 0600); + int fd2 = ::open("out2.ttf", O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd1 < 0 || fd2 < 0) { + ::perror("opening output file"); + return 1; + } + if ((::write(fd1, result, result_len) < 0) || + (::write(fd2, result2, result2_len) < 0)) { + ::perror("writing output file"); + return 1; + } + } + + return 0; +} diff --git a/test/ot-sanitise.cc b/test/ot-sanitise.cc new file mode 100644 index 0000000..313a6bc --- /dev/null +++ b/test/ot-sanitise.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A very simple driver program while sanitises the file given as argv[1] and +// writes the sanitised version to stdout. + +#include +#include +#include + +#include +#include + +#include "file-stream.h" +#include "opentype-sanitiser.h" + +namespace { + +int Usage(const char *argv0) { + std::fprintf(stderr, "Usage: %s ttf_file > dest_ttf_file\n", argv0); + return 1; +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) return Usage(argv[0]); + if (::isatty(1)) return Usage(argv[0]); + + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + + uint8_t *data = new uint8_t[st.st_size]; + if (::read(fd, data, st.st_size) != st.st_size) { + ::perror("read"); + return 1; + } + ::close(fd); + + ots::FILEStream output(stdout); + const bool result = ots::Process(&output, data, st.st_size); + + if (!result) { + std::fprintf(stderr, "Failed to sanitise file!\n"); + } + return !result; +} diff --git a/test/perf.cc b/test/perf.cc new file mode 100644 index 0000000..34fe931 --- /dev/null +++ b/test/perf.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include // for timersub macro. +#include +#include + +#include +#include +#include + +#include "opentype-sanitiser.h" +#include "ots-memory-stream.h" + +namespace { + +int Usage(const char *argv0) { + std::fprintf(stderr, "Usage: %s \n", argv0); + return 1; +} + +} // namespace + +int main(int argc, char **argv) { + ots::DisableDebugOutput(); // turn off ERROR and WARNING outputs. + + if (argc != 2) return Usage(argv[0]); + + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + + uint8_t *data = new uint8_t[st.st_size]; + if (::read(fd, data, st.st_size) != st.st_size) { + std::fprintf(stderr, "Failed to read file!\n"); + return 1; + } + + // A transcoded font is usually smaller than an original font. + // However, it can be slightly bigger than the original one due to + // name table replacement and/or padding for glyf table. + static const size_t kPadLen = 20 * 1024; + uint8_t *result = new uint8_t[st.st_size + kPadLen]; + + int num_repeat = 250; + if (st.st_size < 1024 * 1024) { + num_repeat = 2500; + } + if (st.st_size < 1024 * 100) { + num_repeat = 5000; + } + + struct timeval start, end, elapsed; + ::gettimeofday(&start, 0); + for (int i = 0; i < num_repeat; ++i) { + ots::MemoryStream output(result, st.st_size + kPadLen); + bool r = ots::Process(&output, data, st.st_size); + if (!r) { + std::fprintf(stderr, "Failed to sanitise file!\n"); + return 1; + } + } + ::gettimeofday(&end, 0); + timersub(&end, &start, &elapsed); + + uint64_t us = ((elapsed.tv_sec * 1000 * 1000) + elapsed.tv_usec) / num_repeat; + std::fprintf(stderr, "%lu [us] %s (%lu bytes, %lu [byte/us])\n", + us, argv[1], st.st_size, (us ? st.st_size / us : 0)); + + return 0; +} diff --git a/test/side-by-side.cc b/test/side-by-side.cc new file mode 100644 index 0000000..1b5ff90 --- /dev/null +++ b/test/side-by-side.cc @@ -0,0 +1,281 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include FT_FREETYPE_H +#include +#include + +#include +#include +#include + +#include "opentype-sanitiser.h" +#include "ots-memory-stream.h" + +namespace { + +void DumpBitmap(const FT_Bitmap *bitmap) { + for (int i = 0; i < bitmap->rows * bitmap->width; ++i) { + if (bitmap->buffer[i] > 192) { + std::fprintf(stderr, "#"); + } else if (bitmap->buffer[i] > 128) { + std::fprintf(stderr, "*"); + } else if (bitmap->buffer[i] > 64) { + std::fprintf(stderr, "+"); + } else if (bitmap->buffer[i] > 32) { + std::fprintf(stderr, "."); + } else { + std::fprintf(stderr, " "); + } + + if ((i + 1) % bitmap->width == 0) { + std::fprintf(stderr, "\n"); + } + } +} + +int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) { + int ret = 0; + + if (orig->width == trans->width && + orig->rows == trans->rows) { + for (int i = 0; i < orig->rows * orig->width; ++i) { + if (orig->buffer[i] != trans->buffer[i]) { + std::fprintf(stderr, "bitmap data doesn't match!\n"); + ret = 1; + break; + } + } + } else { + std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n", + orig->width, orig->rows, trans->width, trans->rows); + ret = 1; + } + + if (ret) { + std::fprintf(stderr, "EXPECTED:\n"); + DumpBitmap(orig); + std::fprintf(stderr, "\nACTUAL:\n"); + DumpBitmap(trans); + std::fprintf(stderr, "\n\n"); + } + + delete[] orig->buffer; + delete[] trans->buffer; + return ret; +} + +int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) { + FT_BBox bbox; + FT_Outline_Get_CBox(outline, &bbox); + + bbox.xMin &= ~63; + bbox.yMin &= ~63; + bbox.xMax = (bbox.xMax + 63) & ~63; + bbox.yMax = (bbox.yMax + 63) & ~63; + FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin); + + const int w = (bbox.xMax - bbox.xMin) >> 6; + const int h = (bbox.yMax - bbox.yMin) >> 6; + + if (w == 0 || h == 0) { + return -1; // white space + } + if (w < 0 || h < 0) { + std::fprintf(stderr, "bad width/height\n"); + return 1; // error + } + + uint8_t *buf = new uint8_t[w * h]; + std::memset(buf, 0x0, w * h); + + bitmap->width = w; + bitmap->rows = h; + bitmap->pitch = w; + bitmap->buffer = buf; + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap->num_grays = 256; + if (FT_Outline_Get_Bitmap(library, outline, bitmap)) { + std::fprintf(stderr, "can't get outline\n"); + delete[] buf; + return 1; // error. + } + + return 0; +} + +int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) { + static const int kDpi = 72; + + FT_Matrix matrix; + matrix.xx = matrix.yy = 1 << 16; + matrix.xy = matrix.yx = 0 << 16; + + FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL; + if (!use_bitmap) { + // Since the transcoder drops embedded bitmaps from the transcoded one, + // we have to use FT_LOAD_NO_BITMAP flag for the original face. + flags |= FT_LOAD_NO_BITMAP; + } + + FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0); + if (error) { + std::fprintf(stderr, "Failed to set the char size!\n"); + return 1; + } + + FT_Set_Transform(face, &matrix, 0); + + error = FT_Load_Char(face, c, flags); + if (error) return -1; // no such glyf in the font. + + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) { + std::fprintf(stderr, "bad format\n"); + return 1; + } + + return 0; +} + +int LoadCharThenCompare(FT_Library library, + FT_Face orig_face, FT_Face trans_face, + int pt, FT_ULong c) { + FT_Bitmap orig_bitmap, trans_bitmap; + + // Load original bitmap. + int ret = LoadChar(orig_face, false, pt, c); + if (ret) return ret; // 1: error, -1: no such glyph + + FT_Outline *outline = &orig_face->glyph->outline; + ret = GetBitmap(library, outline, &orig_bitmap); + if (ret) return ret; // white space? + + // Load transformed bitmap. + ret = LoadChar(trans_face, true, pt, c); + if (ret == -1) { + std::fprintf(stderr, "the glyph is not found on the transcoded font\n"); + } + if (ret) return 1; // -1 should be treated as error. + outline = &trans_face->glyph->outline; + ret = GetBitmap(library, outline, &trans_bitmap); + if (ret) return ret; // white space? + + return CompareBitmaps(&orig_bitmap, &trans_bitmap); +} + +int SideBySide(FT_Library library, const char *file_name, + uint8_t *orig_font, size_t orig_len, + uint8_t *trans_font, size_t trans_len) { + FT_Face orig_face; + FT_Error error + = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face); + if (error) { + std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name); + return 1; + } + + FT_Face trans_face; + error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face); + if (error) { + std::fprintf(stderr, "Failed to open the transcoded font: %s!\n", + file_name); + return 1; + } + + static const int kPts[] = {100, 20, 18, 16, 12, 10, 8}; // pt + static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]); + + static const int kUnicodeRanges[] = { + 0x0020, 0x007E, // Basic Latin (ASCII) + 0x00A1, 0x017F, // Latin-1 + 0x1100, 0x11FF, // Hangul + 0x3040, 0x309F, // Japanese HIRAGANA letters + 0x3130, 0x318F, // Hangul + 0x4E00, 0x4F00, // CJK Kanji/Hanja + 0xAC00, 0xAD00, // Hangul + }; + static const size_t kUnicodeRangesLen + = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]); + + for (size_t i = 0; i < kPtsLen; ++i) { + for (size_t j = 0; j < kUnicodeRangesLen; j += 2) { + for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) { + int ret = LoadCharThenCompare(library, orig_face, trans_face, + kPts[i], + kUnicodeRanges[j] + k); + if (ret > 0) { + std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n", + file_name, kUnicodeRanges[j] + k, kPts[i]); + return 1; + } + } + } + } + + return 0; +} + +} // namespace + +int main(int argc, char **argv) { + ots::DisableDebugOutput(); // turn off ERROR and WARNING outputs. + + if (argc != 2) { + std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]); + return 1; + } + + // load the font to memory. + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + const off_t orig_len = st.st_size; + + uint8_t *orig_font = new uint8_t[orig_len]; + if (::read(fd, orig_font, orig_len) != orig_len) { + std::fprintf(stderr, "Failed to read file!\n"); + return 1; + } + ::close(fd); + + // check if FreeType2 can open the original font. + FT_Library library; + FT_Error error = FT_Init_FreeType(&library); + if (error) { + std::fprintf(stderr, "Failed to initialize FreeType2!\n"); + return 1; + } + FT_Face dummy; + error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy); + if (error) { + std::fprintf(stderr, "Failed to open the original font with FT2! %s\n", + argv[1]); + return 1; + } + + // transcode the original font. + static const size_t kPadLen = 20 * 1024; + uint8_t *trans_font = new uint8_t[orig_len + kPadLen]; + ots::MemoryStream output(trans_font, orig_len + kPadLen); + + bool result = ots::Process(&output, orig_font, orig_len); + if (!result) { + std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]); + return 1; + } + const size_t trans_len = output.Tell(); + + // perform side-by-side tests. + return SideBySide(library, argv[1], + orig_font, orig_len, + trans_font, trans_len); +} diff --git a/test/validator-checker.cc b/test/validator-checker.cc new file mode 100644 index 0000000..66c2b75 --- /dev/null +++ b/test/validator-checker.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include FT_FREETYPE_H +#include +#include + +#include +#include +#include + +#include "opentype-sanitiser.h" +#include "ots-memory-stream.h" + +namespace { + +void LoadChar(FT_Face face, int pt, FT_ULong c) { + FT_Matrix matrix; + matrix.xx = matrix.yy = 1 << 16; + matrix.xy = matrix.yx = 0 << 16; + + FT_Set_Char_Size(face, pt * (1 << 6), 0, 72, 0); + FT_Set_Transform(face, &matrix, 0); + FT_Load_Char(face, c, FT_LOAD_RENDER); +} + +int OpenAndLoadChars(FT_Library library, const char *file_name, + uint8_t *trans_font, size_t trans_len) { + FT_Face trans_face; + FT_Error error + = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face); + if (error) { + std::fprintf(stderr, + "OK: FreeType2 couldn't open the transcoded font: %s\n", + file_name); + return 0; + } + + static const int kPts[] = {100, 20, 18, 16, 12, 10, 8}; // pt + static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]); + + static const int kUnicodeRanges[] = { + 0x0020, 0x007E, // Basic Latin (ASCII) + 0x00A1, 0x017F, // Latin-1 + 0x1100, 0x11FF, // Hangul + 0x3040, 0x309F, // Japanese HIRAGANA letters + 0x3130, 0x318F, // Hangul + 0x4E00, 0x4F00, // CJK Kanji/Hanja + 0xAC00, 0xAD00, // Hangul + }; + static const size_t kUnicodeRangesLen + = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]); + + for (size_t i = 0; i < kPtsLen; ++i) { + for (size_t j = 0; j < kUnicodeRangesLen; j += 2) { + for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) { + LoadChar(trans_face, kPts[i], kUnicodeRanges[j] + k); + } + } + } + + std::fprintf(stderr, "OK: FreeType2 didn't crash: %s\n", file_name); + return 0; +} + +} // namespace + +int main(int argc, char **argv) { + ots::DisableDebugOutput(); // turn off ERROR and WARNING outputs. + + if (argc != 2) { + std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]); + return 1; + } + + // load the font to memory. + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + const off_t orig_len = st.st_size; + + uint8_t *orig_font = new uint8_t[orig_len]; + if (::read(fd, orig_font, orig_len) != orig_len) { + std::fprintf(stderr, "Failed to read file!\n"); + return 1; + } + ::close(fd); + + // transcode the malicious font. + static const size_t kBigPadLen = 1024 * 1024; // 1MB + uint8_t *trans_font = new uint8_t[orig_len + kBigPadLen]; + ots::MemoryStream output(trans_font, orig_len + kBigPadLen); + + bool result = ots::Process(&output, orig_font, orig_len); + if (!result) { + std::fprintf(stderr, "OK: the malicious font was filtered: %s\n", argv[1]); + return 0; + } + const size_t trans_len = output.Tell(); + + FT_Library library; + FT_Error error = FT_Init_FreeType(&library); + if (error) { + std::fprintf(stderr, "Failed to initialize FreeType2!\n"); + return 1; + } + + return OpenAndLoadChars(library, argv[1], trans_font, trans_len); +} -- cgit v1.2.3