1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if !defined(_WIN32)
6#ifdef __linux__
7// Linux
8#include <freetype/ftoutln.h>
9#include <ft2build.h>
10#include FT_FREETYPE_H
11#else
12// Mac OS X
13#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
14#endif  // __linux__
15#include <unistd.h>
16#else
17// Windows
18#include <io.h>
19#include <Windows.h>
20#endif  // !defiend(_WIN32)
21
22#include <fcntl.h>
23#include <sys/stat.h>
24
25#include <cstdio>
26#include <cstdlib>
27#include <cstring>
28
29#include "opentype-sanitiser.h"
30#include "ots-memory-stream.h"
31
32namespace {
33
34int Usage(const char *argv0) {
35  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
36  return 1;
37}
38
39bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size);
40bool DumpResults(const uint8_t *result1, const size_t len1,
41                 const uint8_t *result2, const size_t len2);
42
43#if defined(_WIN32)
44#define ADDITIONAL_OPEN_FLAGS O_BINARY
45#else
46#define ADDITIONAL_OPEN_FLAGS 0
47#endif
48
49bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size) {
50  const int fd = open(file_name, O_RDONLY | ADDITIONAL_OPEN_FLAGS);
51  if (fd < 0) {
52    return false;
53  }
54
55  struct stat st;
56  fstat(fd, &st);
57
58  *file_size = st.st_size;
59  *data = new uint8_t[st.st_size];
60  if (read(fd, *data, st.st_size) != st.st_size) {
61    close(fd);
62    return false;
63  }
64  close(fd);
65  return true;
66}
67
68bool DumpResults(const uint8_t *result1, const size_t len1,
69                 const uint8_t *result2, const size_t len2) {
70  int fd1 = open("out1.ttf",
71                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
72  int fd2 = open("out2.ttf",
73                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
74  if (fd1 < 0 || fd2 < 0) {
75    perror("opening output file");
76    return false;
77  }
78  if ((write(fd1, result1, len1) < 0) ||
79      (write(fd2, result2, len2) < 0)) {
80    perror("writing output file");
81    close(fd1);
82    close(fd2);
83    return false;
84  }
85  close(fd1);
86  close(fd2);
87  return true;
88}
89
90// Platform specific implementations.
91bool VerifyTranscodedFont(uint8_t *result, const size_t len);
92
93#if defined(__linux__)
94// Linux
95bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
96  FT_Library library;
97  FT_Error error = ::FT_Init_FreeType(&library);
98  if (error) {
99    return false;
100  }
101  FT_Face dummy;
102  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
103  if (error) {
104    return false;
105  }
106  ::FT_Done_Face(dummy);
107  return true;
108}
109
110#elif defined(__APPLE_CC__)
111// Mac
112bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
113  CFDataRef data = CFDataCreate(0, result, len);
114  if (!data) {
115    return false;
116  }
117
118  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
119  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
120  CGDataProviderRelease(dataProvider);
121  CFRelease(data);
122  if (!cgFontRef) {
123    return false;
124  }
125
126  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
127  CGFontRelease(cgFontRef);
128  if (!numGlyphs) {
129    return false;
130  }
131  return true;
132}
133
134#elif defined(_WIN32)
135// Windows
136bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
137  DWORD num_fonts = 0;
138  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
139  if (!handle) {
140    return false;
141  }
142  RemoveFontMemResourceEx(handle);
143  return true;
144}
145
146#else
147bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
148  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
149  return false;
150}
151
152#endif
153
154}  // namespace
155
156int main(int argc, char **argv) {
157  if (argc != 2) return Usage(argv[0]);
158
159  size_t file_size = 0;
160  uint8_t *data = 0;
161  if (!ReadFile(argv[1], &data, &file_size)) {
162    std::fprintf(stderr, "Failed to read file!\n");
163    return 1;
164  }
165
166  // A transcoded font is usually smaller than an original font.
167  // However, it can be slightly bigger than the original one due to
168  // name table replacement and/or padding for glyf table.
169  //
170  // However, a WOFF font gets decompressed and so can be *much* larger than
171  // the original.
172  uint8_t *result = new uint8_t[file_size * 8];
173  ots::MemoryStream output(result, file_size * 8);
174
175  bool r = ots::Process(&output, data, file_size);
176  if (!r) {
177    std::fprintf(stderr, "Failed to sanitise file!\n");
178    return 1;
179  }
180  const size_t result_len = output.Tell();
181  delete[] data;
182
183  uint8_t *result2 = new uint8_t[result_len];
184  ots::MemoryStream output2(result2, result_len);
185  r = ots::Process(&output2, result, result_len);
186  if (!r) {
187    std::fprintf(stderr, "Failed to sanitise previous output!\n");
188    return 1;
189  }
190  const size_t result2_len = output2.Tell();
191
192  bool dump_results = false;
193  if (result2_len != result_len) {
194    std::fprintf(stderr, "Outputs differ in length\n");
195    dump_results = true;
196  } else if (std::memcmp(result2, result, result_len)) {
197    std::fprintf(stderr, "Outputs differ in content\n");
198    dump_results = true;
199  }
200
201  if (dump_results) {
202    std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n");
203    if (!DumpResults(result, result_len, result2, result2_len)) {
204      std::fprintf(stderr, "Failed to dump output files.\n");
205      return 1;
206    }
207  }
208
209  // Verify that the transcoded font can be opened by the font renderer for
210  // Linux (FreeType2), Mac OS X, or Windows.
211  if (!VerifyTranscodedFont(result, result_len)) {
212    std::fprintf(stderr, "Failed to verify the transcoded font\n");
213    return 1;
214  }
215
216  return 0;
217}
218