| Index: third_party/ots/test/idempotent.cc
 | 
| diff --git a/third_party/ots/test/idempotent.cc b/third_party/ots/test/idempotent.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..ec50ab44bd4726526d22263f50431caaa5e7c98a
 | 
| --- /dev/null
 | 
| +++ b/third_party/ots/test/idempotent.cc
 | 
| @@ -0,0 +1,219 @@
 | 
| +// 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.
 | 
| +
 | 
| +#if !defined(_WIN32)
 | 
| +#ifdef __linux__
 | 
| +// Linux
 | 
| +#include <ft2build.h>
 | 
| +#include FT_FREETYPE_H
 | 
| +#include FT_OUTLINE_H
 | 
| +#else
 | 
| +// Mac OS X
 | 
| +#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
 | 
| +#endif  // __linux__
 | 
| +#include <unistd.h>
 | 
| +#else
 | 
| +// Windows
 | 
| +#include <io.h>
 | 
| +#include <Windows.h>
 | 
| +#endif  // !defiend(_WIN32)
 | 
| +
 | 
| +#include <fcntl.h>
 | 
| +#include <sys/stat.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;
 | 
| +}
 | 
| +
 | 
| +bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size);
 | 
| +bool DumpResults(const uint8_t *result1, const size_t len1,
 | 
| +                 const uint8_t *result2, const size_t len2);
 | 
| +
 | 
| +#if defined(_WIN32)
 | 
| +#define ADDITIONAL_OPEN_FLAGS O_BINARY
 | 
| +#else
 | 
| +#define ADDITIONAL_OPEN_FLAGS 0
 | 
| +#endif
 | 
| +
 | 
| +bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size) {
 | 
| +  const int fd = open(file_name, O_RDONLY | ADDITIONAL_OPEN_FLAGS);
 | 
| +  if (fd < 0) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  struct stat st;
 | 
| +  fstat(fd, &st);
 | 
| +
 | 
| +  *file_size = st.st_size;
 | 
| +  *data = new uint8_t[st.st_size];
 | 
| +  if (read(fd, *data, st.st_size) != st.st_size) {
 | 
| +    close(fd);
 | 
| +    return false;
 | 
| +  }
 | 
| +  close(fd);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool DumpResults(const uint8_t *result1, const size_t len1,
 | 
| +                 const uint8_t *result2, const size_t len2) {
 | 
| +  int fd1 = open("out1.ttf",
 | 
| +                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
 | 
| +  int fd2 = open("out2.ttf",
 | 
| +                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
 | 
| +  if (fd1 < 0 || fd2 < 0) {
 | 
| +    perror("opening output file");
 | 
| +    return false;
 | 
| +  }
 | 
| +  if ((write(fd1, result1, len1) < 0) ||
 | 
| +      (write(fd2, result2, len2) < 0)) {
 | 
| +    perror("writing output file");
 | 
| +    close(fd1);
 | 
| +    close(fd2);
 | 
| +    return false;
 | 
| +  }
 | 
| +  close(fd1);
 | 
| +  close(fd2);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +// Platform specific implementations.
 | 
| +bool VerifyTranscodedFont(uint8_t *result, const size_t len);
 | 
| +
 | 
| +#if defined(__linux__)
 | 
| +// Linux
 | 
| +bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
 | 
| +  FT_Library library;
 | 
| +  FT_Error error = ::FT_Init_FreeType(&library);
 | 
| +  if (error) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  FT_Face dummy;
 | 
| +  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
 | 
| +  if (error) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  ::FT_Done_Face(dummy);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +#elif defined(__APPLE_CC__)
 | 
| +// Mac
 | 
| +bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
 | 
| +  CFDataRef data = CFDataCreate(0, result, len);
 | 
| +  if (!data) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
 | 
| +  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
 | 
| +  CGDataProviderRelease(dataProvider);
 | 
| +  CFRelease(data);
 | 
| +  if (!cgFontRef) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
 | 
| +  CGFontRelease(cgFontRef);
 | 
| +  if (!numGlyphs) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +#elif defined(_WIN32)
 | 
| +// Windows
 | 
| +bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
 | 
| +  DWORD num_fonts = 0;
 | 
| +  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
 | 
| +  if (!handle) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  RemoveFontMemResourceEx(handle);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +#else
 | 
| +bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
 | 
| +  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
 | 
| +  return false;
 | 
| +}
 | 
| +
 | 
| +#endif
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +int main(int argc, char **argv) {
 | 
| +  if (argc != 2) return Usage(argv[0]);
 | 
| +
 | 
| +  size_t file_size = 0;
 | 
| +  uint8_t *data = 0;
 | 
| +  if (!ReadFile(argv[1], &data, &file_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.
 | 
| +  //
 | 
| +  // However, a WOFF font gets decompressed and so can be *much* larger than
 | 
| +  // the original.
 | 
| +  uint8_t *result = new uint8_t[file_size * 8];
 | 
| +  ots::MemoryStream output(result, file_size * 8);
 | 
| +
 | 
| +  ots::OTSContext context;
 | 
| +
 | 
| +  bool r = context.Process(&output, data, file_size);
 | 
| +  if (!r) {
 | 
| +    std::fprintf(stderr, "Failed to sanitise file!\n");
 | 
| +    return 1;
 | 
| +  }
 | 
| +  const size_t result_len = output.Tell();
 | 
| +  delete[] data;
 | 
| +
 | 
| +  uint8_t *result2 = new uint8_t[result_len];
 | 
| +  ots::MemoryStream output2(result2, result_len);
 | 
| +  r = context.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");
 | 
| +    if (!DumpResults(result, result_len, result2, result2_len)) {
 | 
| +      std::fprintf(stderr, "Failed to dump output files.\n");
 | 
| +      return 1;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Verify that the transcoded font can be opened by the font renderer for
 | 
| +  // Linux (FreeType2), Mac OS X, or Windows.
 | 
| +  if (!VerifyTranscodedFont(result, result_len)) {
 | 
| +    std::fprintf(stderr, "Failed to verify the transcoded font\n");
 | 
| +    return 1;
 | 
| +  }
 | 
| +
 | 
| +  return 0;
 | 
| +}
 | 
| 
 |