aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authoryusukes@chromium.org <yusukes@chromium.org@a4e77c2c-9104-11de-800e-5b313e0d2bf3>2009-11-04 04:56:32 +0000
committeryusukes@chromium.org <yusukes@chromium.org@a4e77c2c-9104-11de-800e-5b313e0d2bf3>2009-11-04 04:56:32 +0000
commitd257d186ae2a08042a412824678f98241a1a4f3c (patch)
tree54b0cf89c22d1a1de3d7866f01d8a98dcbe8565f /test
parent11e88a133d0bc47723c2e9e06df471e2f60bb350 (diff)
downloadots-d257d186ae2a08042a412824678f98241a1a4f3c.tar.gz
initial commit.
git-svn-id: http://ots.googlecode.com/svn/trunk@2 a4e77c2c-9104-11de-800e-5b313e0d2bf3
Diffstat (limited to 'test')
-rw-r--r--test/README243
-rw-r--r--test/SConstruct43
-rw-r--r--test/file-stream.h39
-rw-r--r--test/idempotent.cc94
-rw-r--r--test/ot-sanitise.cc54
-rw-r--r--test/perf.cc79
-rw-r--r--test/side-by-side.cc281
-rw-r--r--test/validator-checker.cc117
8 files changed, 950 insertions, 0 deletions
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<unnamed>::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<unnamed>::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 <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+ std::fprintf(stderr, "Usage: %s <ttf file>\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 <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+
+#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 <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h> // for timersub macro.
+#include <unistd.h>
+#include <time.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+ std::fprintf(stderr, "Usage: %s <ttf file>\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 <fcntl.h>
+#include <freetype/ftoutln.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#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 <fcntl.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#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);
+}