Home | History | Annotate | Download | only in test
      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 #include <fcntl.h>
      6 #include <freetype/ftoutln.h>
      7 #include <ft2build.h>
      8 #include FT_FREETYPE_H
      9 #include <sys/stat.h>
     10 #include <sys/types.h>
     11 #include <unistd.h>
     12 
     13 #include <cstdio>
     14 #include <cstdlib>
     15 #include <cstring>
     16 
     17 #include "opentype-sanitiser.h"
     18 #include "ots-memory-stream.h"
     19 
     20 namespace {
     21 
     22 void DumpBitmap(const FT_Bitmap *bitmap) {
     23   for (int i = 0; i < bitmap->rows * bitmap->width; ++i) {
     24     if (bitmap->buffer[i] > 192) {
     25       std::fprintf(stderr, "#");
     26     } else if (bitmap->buffer[i] > 128) {
     27       std::fprintf(stderr, "*");
     28     } else if (bitmap->buffer[i] > 64) {
     29       std::fprintf(stderr, "+");
     30     } else if (bitmap->buffer[i] > 32) {
     31       std::fprintf(stderr, ".");
     32     } else {
     33       std::fprintf(stderr, " ");
     34     }
     35 
     36     if ((i + 1) % bitmap->width == 0) {
     37       std::fprintf(stderr, "\n");
     38     }
     39   }
     40 }
     41 
     42 int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
     43   int ret = 0;
     44 
     45   if (orig->width == trans->width &&
     46       orig->rows == trans->rows) {
     47     for (int i = 0; i < orig->rows * orig->width; ++i) {
     48       if (orig->buffer[i] != trans->buffer[i]) {
     49         std::fprintf(stderr, "bitmap data doesn't match!\n");
     50         ret = 1;
     51         break;
     52       }
     53     }
     54   } else {
     55     std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
     56                  orig->width, orig->rows, trans->width, trans->rows);
     57     ret = 1;
     58   }
     59 
     60   if (ret) {
     61     std::fprintf(stderr, "EXPECTED:\n");
     62     DumpBitmap(orig);
     63     std::fprintf(stderr, "\nACTUAL:\n");
     64     DumpBitmap(trans);
     65     std::fprintf(stderr, "\n\n");
     66   }
     67 
     68   delete[] orig->buffer;
     69   delete[] trans->buffer;
     70   return ret;
     71 }
     72 
     73 int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
     74   FT_BBox bbox;
     75   FT_Outline_Get_CBox(outline, &bbox);
     76 
     77   bbox.xMin &= ~63;
     78   bbox.yMin &= ~63;
     79   bbox.xMax = (bbox.xMax + 63) & ~63;
     80   bbox.yMax = (bbox.yMax + 63) & ~63;
     81   FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);
     82 
     83   const int w = (bbox.xMax - bbox.xMin) >> 6;
     84   const int h = (bbox.yMax - bbox.yMin) >> 6;
     85 
     86   if (w == 0 || h == 0) {
     87     return -1;  // white space
     88   }
     89   if (w < 0 || h < 0) {
     90     std::fprintf(stderr, "bad width/height\n");
     91     return 1;  // error
     92   }
     93 
     94   uint8_t *buf = new uint8_t[w * h];
     95   std::memset(buf, 0x0, w * h);
     96 
     97   bitmap->width = w;
     98   bitmap->rows = h;
     99   bitmap->pitch = w;
    100   bitmap->buffer = buf;
    101   bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
    102   bitmap->num_grays = 256;
    103   if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
    104     std::fprintf(stderr, "can't get outline\n");
    105     delete[] buf;
    106     return 1;  // error.
    107   }
    108 
    109   return 0;
    110 }
    111 
    112 int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
    113   static const int kDpi = 72;
    114 
    115   FT_Matrix matrix;
    116   matrix.xx = matrix.yy = 1 << 16;
    117   matrix.xy = matrix.yx = 0 << 16;
    118 
    119   FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
    120   if (!use_bitmap) {
    121     // Since the transcoder drops embedded bitmaps from the transcoded one,
    122     // we have to use FT_LOAD_NO_BITMAP flag for the original face.
    123     flags |= FT_LOAD_NO_BITMAP;
    124   }
    125 
    126   FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
    127   if (error) {
    128     std::fprintf(stderr, "Failed to set the char size!\n");
    129     return 1;
    130   }
    131 
    132   FT_Set_Transform(face, &matrix, 0);
    133 
    134   error = FT_Load_Char(face, c, flags);
    135   if (error) return -1;  // no such glyf in the font.
    136 
    137   if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
    138     std::fprintf(stderr, "bad format\n");
    139     return 1;
    140   }
    141 
    142   return 0;
    143 }
    144 
    145 int LoadCharThenCompare(FT_Library library,
    146                         FT_Face orig_face, FT_Face trans_face,
    147                         int pt, FT_ULong c) {
    148   FT_Bitmap orig_bitmap, trans_bitmap;
    149 
    150   // Load original bitmap.
    151   int ret = LoadChar(orig_face, false, pt, c);
    152   if (ret) return ret;  // 1: error, -1: no such glyph
    153 
    154   FT_Outline *outline = &orig_face->glyph->outline;
    155   ret = GetBitmap(library, outline, &orig_bitmap);
    156   if (ret) return ret;  // white space?
    157 
    158   // Load transformed bitmap.
    159   ret = LoadChar(trans_face, true, pt, c);
    160   if (ret == -1) {
    161     std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
    162   }
    163   if (ret) return 1;  // -1 should be treated as error.
    164   outline = &trans_face->glyph->outline;
    165   ret = GetBitmap(library, outline, &trans_bitmap);
    166   if (ret) return ret;  // white space?
    167 
    168   return CompareBitmaps(&orig_bitmap, &trans_bitmap);
    169 }
    170 
    171 int SideBySide(FT_Library library, const char *file_name,
    172                uint8_t *orig_font, size_t orig_len,
    173                uint8_t *trans_font, size_t trans_len) {
    174   FT_Face orig_face;
    175   FT_Error error
    176       = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
    177   if (error) {
    178     std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
    179     return 1;
    180   }
    181 
    182   FT_Face trans_face;
    183   error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
    184   if (error) {
    185     std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
    186                  file_name);
    187     return 1;
    188   }
    189 
    190   static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
    191   static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
    192 
    193   static const int kUnicodeRanges[] = {
    194     0x0020, 0x007E,  // Basic Latin (ASCII)
    195     0x00A1, 0x017F,  // Latin-1
    196     0x1100, 0x11FF,  // Hangul
    197     0x3040, 0x309F,  // Japanese HIRAGANA letters
    198     0x3130, 0x318F,  // Hangul
    199     0x4E00, 0x4F00,  // CJK Kanji/Hanja
    200     0xAC00, 0xAD00,  // Hangul
    201   };
    202   static const size_t kUnicodeRangesLen
    203       = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
    204 
    205   for (size_t i = 0; i < kPtsLen; ++i) {
    206     for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
    207       for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
    208         int ret = LoadCharThenCompare(library, orig_face, trans_face,
    209                                       kPts[i],
    210                                       kUnicodeRanges[j] + k);
    211         if (ret > 0) {
    212           std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
    213                        file_name, kUnicodeRanges[j] + k, kPts[i]);
    214           return 1;
    215         }
    216       }
    217     }
    218   }
    219 
    220   return 0;
    221 }
    222 
    223 }  // namespace
    224 
    225 int main(int argc, char **argv) {
    226   ots::DisableDebugOutput();  // turn off ERROR and WARNING outputs.
    227 
    228   if (argc != 2) {
    229     std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
    230     return 1;
    231   }
    232 
    233   // load the font to memory.
    234   const int fd = ::open(argv[1], O_RDONLY);
    235   if (fd < 0) {
    236     ::perror("open");
    237     return 1;
    238   }
    239 
    240   struct stat st;
    241   ::fstat(fd, &st);
    242   const off_t orig_len = st.st_size;
    243 
    244   uint8_t *orig_font = new uint8_t[orig_len];
    245   if (::read(fd, orig_font, orig_len) != orig_len) {
    246     std::fprintf(stderr, "Failed to read file!\n");
    247     return 1;
    248   }
    249   ::close(fd);
    250 
    251   // check if FreeType2 can open the original font.
    252   FT_Library library;
    253   FT_Error error = FT_Init_FreeType(&library);
    254   if (error) {
    255     std::fprintf(stderr, "Failed to initialize FreeType2!\n");
    256     return 1;
    257   }
    258   FT_Face dummy;
    259   error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
    260   if (error) {
    261     std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
    262                  argv[1]);
    263     return 1;
    264   }
    265 
    266   // transcode the original font.
    267   static const size_t kPadLen = 20 * 1024;
    268   uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
    269   ots::MemoryStream output(trans_font, orig_len + kPadLen);
    270 
    271   bool result = ots::Process(&output, orig_font, orig_len);
    272   if (!result) {
    273     std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
    274     return 1;
    275   }
    276   const size_t trans_len = output.Tell();
    277 
    278   // perform side-by-side tests.
    279   return SideBySide(library, argv[1],
    280                     orig_font, orig_len,
    281                     trans_font, trans_len);
    282 }
    283