1 // main.cpp
2 
3 // Copyright (C) 2011-2021 by Werner Lemberg.
4 //
5 // This file is part of the ttfautohint library, and may only be used,
6 // modified, and distributed under the terms given in `COPYING'.  By
7 // continuing to use, modify, or distribute this file you indicate that you
8 // have read `COPYING' and understand and accept it fully.
9 //
10 // The file `COPYING' mentioned in the previous paragraph is distributed
11 // with the ttfautohint library.
12 
13 
14 // This program is a wrapper for `TTF_autohint'.
15 
16 #ifdef BUILD_GUI
17 #  ifndef _WIN32
18 #    define CONSOLE_OUTPUT
19 #  endif
20 #else
21 #  define CONSOLE_OUTPUT
22 #endif
23 
24 #include <config.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <getopt.h>
31 #include <limits.h>
32 #include <unistd.h>
33 #include <locale.h>
34 
35 #include <vector>
36 #include <string>
37 
38 #ifdef BUILD_GUI
39 #  include <QApplication>
40 #  include "maingui.h"
41 #else
42 #  include <ft2build.h>
43 #  include FT_FREETYPE_H
44 #  include FT_TRUETYPE_TABLES_H // for option `-T'
45 #  include "info.h"
46 #endif
47 
48 #include <ttfautohint.h>
49 #include <numberset.h>
50 
51 
52 #ifndef BUILD_GUI
53 #  ifdef _WIN32
54 #    include <fcntl.h>
55 #    define SET_BINARY(f) do { \
56                             if (!isatty(fileno(f))) \
57                               setmode(fileno(f), O_BINARY); \
58                           } while (0)
59 #  endif
60 
61 #  ifndef SET_BINARY
62 #    define SET_BINARY(f) do {} while (0)
63 #  endif
64 #endif
65 
66 
67 using namespace std;
68 
69 
70 typedef struct Tag_Names_
71 {
72   const char* tag;
73   const char* description;
74 } Tag_Names;
75 
76 
77 // the available script tags and its descriptions are directly extracted
78 // from `ttfautohint-scripts.h'
79 #undef SCRIPT
80 #define SCRIPT(s, S, d, h, H, ss) \
81           {#s, d},
82 
83 const Tag_Names script_names[] =
84 {
85 #include <ttfautohint-scripts.h>
86   {NULL, NULL}
87 };
88 
89 
90 #ifndef BUILD_GUI
91   // the available feature tags and its descriptions are directly extracted
92   // from `ttfautohint-coverages.h'
93 #  undef COVERAGE
94 #  define COVERAGE(n, N, d, t, t1, t2, t3, t4) \
95             {#t, d},
96 
97 const Tag_Names feature_names[] =
98 {
99 #include <ttfautohint-coverages.h>
100   {NULL, NULL}
101 };
102 #endif
103 
104 
105 #ifndef BUILD_GUI
106 extern "C" {
107 
108 typedef struct Progress_Data_
109 {
110   long last_sfnt;
111   bool begin;
112   int last_percent;
113 } Progress_Data;
114 
115 
116 static int
progress(long curr_idx,long num_glyphs,long curr_sfnt,long num_sfnts,void * user)117 progress(long curr_idx,
118          long num_glyphs,
119          long curr_sfnt,
120          long num_sfnts,
121          void* user)
122 {
123   Progress_Data* data = (Progress_Data*)user;
124 
125   if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
126   {
127     fprintf(stderr, "subfont %ld of %ld\n", curr_sfnt + 1, num_sfnts);
128     data->last_sfnt = curr_sfnt;
129     data->last_percent = 0;
130     data->begin = true;
131   }
132 
133   if (data->begin)
134   {
135     fprintf(stderr, "  %ld glyphs\n"
136                     "   ", num_glyphs);
137     data->begin = false;
138   }
139 
140   // print progress approx. every 10%
141   int curr_percent = int(curr_idx * 100 / num_glyphs);
142   int curr_diff = curr_percent - data->last_percent;
143 
144   if (curr_diff >= 10)
145   {
146     fprintf(stderr, " %d%%", curr_percent);
147     data->last_percent = curr_percent - curr_percent % 10;
148   }
149 
150   if (curr_idx + 1 == num_glyphs)
151     fprintf(stderr, "\n");
152 
153   return 0;
154 }
155 
156 
157 typedef struct Error_Data_
158 {
159   const char* control_name;
160 } Error_Data;
161 
162 
163 static void
err(TA_Error error,const char * error_string,unsigned int errlinenum,const char * errline,const char * errpos,void * user)164 err(TA_Error error,
165     const char* error_string,
166     unsigned int errlinenum,
167     const char* errline,
168     const char* errpos,
169     void* user)
170 {
171   Error_Data* data = static_cast<Error_Data*>(user);
172 
173   if (!error)
174     return;
175 
176   // We replace some terse error strings with more user-friendly versions.
177   if (error == TA_Err_Invalid_FreeType_Version)
178     fprintf(stderr,
179             "FreeType version 2.4.5 or higher is needed.\n"
180             "Perhaps using a wrong FreeType DLL?\n");
181   else if (error == TA_Err_Invalid_Font_Type)
182     fprintf(stderr,
183             "This font is not a valid font"
184               " in SFNT format with TrueType outlines.\n"
185             "In particular, CFF outlines are not supported.\n");
186   else if (error == TA_Err_Already_Processed)
187     fprintf(stderr,
188             "This font has already been processed with ttfautohint.\n");
189   else if (error == TA_Err_Missing_Legal_Permission)
190     fprintf(stderr,
191             "Bit 1 in the `fsType' field of the `OS/2' table is set:\n"
192             "This font must not be modified"
193               " without permission of the legal owner.\n"
194             "Use command line option `-i' to continue"
195               " if you have such a permission.\n");
196   else if (error == TA_Err_Missing_Unicode_CMap)
197     fprintf(stderr,
198             "No Unicode character map.\n");
199   else if (error == TA_Err_Missing_Symbol_CMap)
200     fprintf(stderr,
201             "No symbol character map.\n");
202   else if (error == TA_Err_Missing_Glyph)
203     fprintf(stderr,
204             "No glyph for a standard character"
205               " to derive standard width and height.\n"
206             "Please check the documentation for a list of"
207               " script-specific standard characters,\n"
208             "or use option `--symbol'.\n");
209   else
210   {
211     if (error < 0x100)
212     {
213       fprintf(stderr, "An error with code 0x%02x occurred"
214                         " while autohinting fonts\n",
215                       error);
216       if (error_string)
217         fprintf(stderr, " %s", error_string);
218     }
219     else if (error >= 0x100 && error < 0x200)
220     {
221       fprintf(stderr, "An error with code 0x%03x occurred"
222                         " while parsing the argument of option `-X'",
223                       error);
224       fprintf(stderr, errline ? ":\n" : ".\n");
225 
226       if (errline)
227         fprintf(stderr, "  %s\n", errline);
228       if (errpos && errline)
229         fprintf(stderr, "  %*s\n", int(errpos - errline + 1), "^");
230     }
231     else if (error >= 0x200 && error < 0x300)
232     {
233       fprintf(stderr, "%s:", data->control_name);
234       if (errlinenum)
235         fprintf(stderr, "%u:", errlinenum);
236       if (errpos && errline)
237         fprintf(stderr, "%d:", int(errpos - errline + 1));
238       if (error_string)
239         fprintf(stderr, " %s", error_string);
240       fprintf(stderr, " (0x%02X)\n", error);
241       if (errline)
242         fprintf(stderr, "  %s\n", errline);
243       if (errpos && errline)
244         fprintf(stderr, "  %*s\n", int(errpos - errline + 1), "^");
245     }
246     else if (error >= 0x300 && error < 0x400)
247     {
248       error -= 0x300;
249       fprintf(stderr, "An error with code 0x%02x occurred"
250                         " while loading the reference font\n",
251                       error);
252       if (error_string)
253         fprintf(stderr, " %s", error_string);
254     }
255   }
256 }
257 
258 
259 } // extern "C"
260 #endif // !BUILD_GUI
261 
262 
263 #ifdef CONSOLE_OUTPUT
264 static void
show_help(bool all,bool is_error)265 show_help(bool
266 #ifdef BUILD_GUI
267                all
268 #endif
269                   ,
270           bool is_error)
271 {
272   FILE* handle = is_error ? stderr : stdout;
273 
274   fprintf(handle,
275 #ifdef BUILD_GUI
276 "Usage: ttfautohintGUI [OPTION]...\n"
277 "A GUI application to replace hints in a TrueType font.\n"
278 #else
279 "Usage: ttfautohint [OPTION]... [IN-FILE [OUT-FILE]]\n"
280 "Replace hints in TrueType font IN-FILE and write output to OUT-FILE.\n"
281 "If OUT-FILE is missing, standard output is used instead;\n"
282 "if IN-FILE is missing also, standard input and output are used.\n"
283 #endif
284 "\n"
285 "The new hints are based on FreeType's auto-hinter.\n"
286 "\n"
287 "This program is a simple front-end to the `ttfautohint' library.\n"
288 "\n");
289 
290   fprintf(handle,
291 "Long options can be given with one or two dashes,\n"
292 "and with and without equal sign between option and argument.\n"
293 "This means that the following forms are acceptable:\n"
294 "`-foo=bar', `--foo=bar', `-foo bar', `--foo bar'.\n"
295 "\n"
296 "Mandatory arguments to long options are mandatory for short options too.\n"
297 #ifdef BUILD_GUI
298 "Options not related to Qt or X11 set default values.\n"
299 #endif
300 "\n"
301 );
302 
303   fprintf(handle,
304 "Options:\n"
305 #ifndef BUILD_GUI
306 "      --debug                print debugging information\n"
307 #endif
308 "  -a, --stem-width-mode=S    select stem width mode for grayscale, GDI\n"
309 "                             ClearType, and DW ClearType, where S is a\n"
310 "                             string of three letters with possible values\n"
311 "                             `n' for natural, `q' for quantized, and `s'\n"
312 "                             for strong (default: qsq)\n"
313 "  -c, --composites           hint glyph composites also\n"
314 "  -d, --dehint               remove all hints\n"
315 "  -D, --default-script=S     set default OpenType script (default: latn)\n"
316 "  -f, --fallback-script=S    set fallback script (default: none)\n"
317 "  -F, --family-suffix=S      append suffix to the family name string(s)\n"
318 "                             in the `name' table\n"
319 "  -G, --hinting-limit=N      switch off hinting above this PPEM value\n"
320 "                             (default: %d); value 0 means no limit\n"
321 "  -h, --help                 display this help and exit\n"
322 "  -H, --fallback-stem-width=N\n"
323 "                             set fallback stem width\n"
324 "                             (default: 50 font units at 2048 UPEM)\n"
325 #ifdef BUILD_GUI
326 "      --help-all             show Qt and X11 specific options also\n"
327 #endif
328 "  -i, --ignore-restrictions  override font license restrictions\n"
329 "  -I, --detailed-info        add detailed ttfautohint info\n"
330 "                             to the version string(s) in the `name' table\n"
331 "  -l, --hinting-range-min=N  the minimum PPEM value for hint sets\n"
332 "                             (default: %d)\n"
333 #ifndef BUILD_GUI
334 "  -m, --control-file=FILE    get control instructions from FILE\n"
335 #endif
336 "  -n, --no-info              don't add ttfautohint info\n"
337 "                             to the version string(s) in the `name' table\n"
338 "  -p, --adjust-subglyphs     handle subglyph adjustments in exotic fonts\n",
339           TA_HINTING_LIMIT, TA_HINTING_RANGE_MIN);
340   fprintf(handle,
341 "  -r, --hinting-range-max=N  the maximum PPEM value for hint sets\n"
342 "                             (default: %d)\n"
343 #ifndef BUILD_GUI
344 "  -R, --reference=FILE       derive blue zones from reference font FILE\n"
345 #endif
346 "  -s, --symbol               input is symbol font\n"
347 "  -S, --fallback-scaling     use fallback scaling, not hinting\n"
348 "  -t, --ttfa-table           add TTFA information table\n"
349 #ifndef BUILD_GUI
350 "  -T, --ttfa-info            display TTFA table in IN-FILE and exit\n"
351 #endif
352 "  -v, --verbose              show progress information\n"
353 "  -V, --version              print version information and exit\n"
354 "  -W, --windows-compatibility\n"
355 "                             add blue zones for `usWinAscent' and\n"
356 "                             `usWinDescent' to avoid clipping\n"
357 "  -x, --increase-x-height=N  increase x height for sizes in the range\n"
358 "                             6<=PPEM<=N; value 0 switches off this feature\n"
359 "                             (default: %d)\n"
360 "  -X, --x-height-snapping-exceptions=STRING\n"
361 "                             specify a comma-separated list of\n"
362 "                             x-height snapping exceptions, for example\n"
363 "                             \"-9, 13-17, 19\" (default: \"\")\n"
364 #ifndef BUILD_GUI
365 "  -Z, --reference-index=N    face index of reference font (default: 0)\n"
366 #endif
367 "\n",
368           TA_HINTING_RANGE_MAX, TA_INCREASE_X_HEIGHT);
369 
370 #ifdef BUILD_GUI
371   if (all)
372   {
373     fprintf(handle,
374 "Qt Options:\n"
375 #if QT_VERSION < 0x050000
376 "      --graphicssystem=SYSTEM\n"
377 "                             select a different graphics system backend\n"
378 "                             instead of the default one\n"
379 "                             (possible values: `raster', `opengl')\n"
380 #endif
381 "      --reverse              set layout direction to right-to-left\n");
382     fprintf(handle,
383 #if QT_VERSION < 0x050000
384 "      --session=ID           restore the application for the given ID\n"
385 #endif
386 "      --style=STYLE          set application GUI style\n"
387 "                             (available values like `windows' or `gtk'\n"
388 "                             depend on Qt version and installed plug-ins)\n"
389 "      --stylesheet=SHEET     apply the given Qt stylesheet\n"
390 "                             to the application widgets\n"
391 "\n");
392 
393 #if QT_VERSION < 0x050000
394     fprintf(handle,
395 "X11 options:\n"
396 "      --background=COLOR     set the default background color\n"
397 "                             and an application palette\n"
398 "                             (light and dark shades are calculated)\n"
399 "      --bg=COLOR             same as --background\n"
400 "      --btn=COLOR            set the default button color\n"
401 "      --button=COLOR         same as --btn\n"
402 "      --cmap                 use a private color map on an 8-bit display\n"
403 "      --display=NAME         use the given X-server display\n");
404     fprintf(handle,
405 "      --fg=COLOR             set the default foreground color\n"
406 "      --fn=FONTNAME          set the application font\n"
407 "      --font=FONTNAME        same as --fn\n"
408 "      --foreground=COLOR     same as --fg\n"
409 "      --geometry=GEOMETRY    set the client geometry of first window\n"
410 "      --im=SERVER            set the X Input Method (XIM) server\n"
411 "      --inputstyle=STYLE     set X Input Method input style\n"
412 "                             (possible values: onthespot, overthespot,\n"
413 "                             offthespot, root)\n");
414     fprintf(handle,
415 "      --name=NAME            set the application name\n"
416 "      --ncols=COUNT          limit the number of colors allocated\n"
417 "                             in the color cube on an 8-bit display,\n"
418 "                             if the application is using the\n"
419 "                             QApplication::ManyColor color specification\n"
420 "      --title=TITLE          set the application title (caption)\n"
421 "      --visual=VISUAL        force the application\n"
422 "                             to use the given visual on an 8-bit display\n"
423 "                             (only possible value: TrueColor)\n"
424 "\n");
425 #endif
426   }
427 #endif // BUILD_GUI
428 
429   fprintf(handle,
430 "The program accepts both TTF and TTC files as input.\n"
431 "Use option -i only if you have a legal permission to modify the font.\n"
432 "The used PPEM value for option -p is FUnits per em, normally 2048.\n"
433 "With option -s, use default values for standard stem width and height,\n"
434 "otherwise they are derived from script-specific characters\n"
435 "resembling the shape of character `o'.\n"
436 "\n");
437   fprintf(handle,
438 "A hint set contains the optimal hinting for a certain PPEM value;\n"
439 "the larger the hint set range (as given by options -l and -r),\n"
440 "the more hint sets get computed, usually increasing the output font size.\n"
441 "The `gasp' table of the output file always enables grayscale hinting\n"
442 "for all sizes (limited by option -G, which is handled in the bytecode).\n"
443 "Increasing the value of -G does not increase the output font size.\n"
444 "\n");
445   fprintf(handle,
446 "Options -f and -D take a four-letter string that identifies a script.\n"
447 "Option -f sets the script used as a fallback for glyphs that can't be\n"
448 "associated with a known script.  By default, such glyphs are hinted;\n"
449 "if option -S is set, they are scaled only instead.  Option -D sets the\n"
450 "default script for handling OpenType features.\n"
451 "\n"
452 "Possible four-letter string values are\n"
453 "\n");
454   const Tag_Names* sn = script_names;
455   for(;;)
456   {
457     fprintf(handle, "  %s (%s)",
458             sn->tag, sn->description);
459     sn++;
460     if (sn->tag)
461       fprintf(handle, ",\n");
462     else
463     {
464       fprintf(handle, ".\n");
465       break;
466     }
467   }
468   fprintf(handle,
469 #ifndef BUILD_GUI
470 "\n"
471 "A control instructions file contains entries of the form\n"
472 "\n"
473 "  [<font idx>] <script> <feature> @ <glyph ids>\n"
474 "\n"
475 "  [<font idx>] <script> <feature> w <stem widths>\n"
476 "\n"
477 "  [<font idx>] <glyph id> l|r <points> [(<left offset>,<right offset>)]\n"
478 "\n"
479 "  [<font idx>] <glyph id> n <points>\n"
480 "\n"
481 "  [<font idx>] <glyph id> t|p <points> [x <shift>] [y <shift>] @ <ppems>\n"
482 "\n"
483 "<font idx> is the current subfont, <glyph id> is a glyph name or index,\n"
484 "<glyph ids> is a set of <glyph id>s, <stem widths> is an unordered set of\n"
485 "integer stem widths in font units, <shift> is a real number in px,\n"
486 "<points> and <ppems> are integer ranges as with option `-X'.\n"
487 "\n"
488 "<script> and <feature> are four-letter tags that define a style\n"
489 "the <glyph ids> are assigned to; possible values for <script> are the same\n"
490 "as with option -D, possible values for <feature> are\n"
491 "\n");
492   const Tag_Names* fn = feature_names;
493   for(;;)
494   {
495     fprintf(handle, "  %s (%s)",
496             fn->tag, fn->description);
497     fn++;
498     if (fn->tag)
499       fprintf(handle, ",\n");
500     else
501     {
502       fprintf(handle, ".\n");
503       break;
504     }
505   }
506   fprintf(handle,
507 "\n"
508 "`w' assigns stem widths to a style; the first value sets the default.\n"
509 "`l' (`r') creates one-point segments with direction left (right).\n"
510 "<left offset> and <right offset> specify offsets (in font units)\n"
511 "relative to the corresponding points to give the segments a length.\n"
512 "`n' removes points from horizontal segments, making them `weak' points.\n"
513 "`t' (`p') applies delta exceptions to the given points before (after) IUP.\n"
514 "\n"
515 "`#' starts a line comment, which gets ignored.\n"
516 "Empty lines are ignored, too.\n"
517 "\n"
518 "Key letters `l', `r', `n', `p', `t', `w', `x', and `y'\n"
519 "have the verbose aliases `left', `right', `nodir', `point', `touch',\n"
520 "`width', `xshift', and `yshift', respectively.\n"
521 #endif
522 "\n"
523 #ifdef BUILD_GUI
524 "A command-line version of this program is called `ttfautohint'.\n"
525 #else
526 "A GUI version of this program is called `ttfautohintGUI'.\n"
527 #endif
528 "\n"
529 "Report bugs to: freetype-devel@nongnu.org\n"
530 "\n"
531 "ttfautohint home page: <https://www.freetype.org/ttfautohint>\n");
532 
533   if (is_error)
534     exit(EXIT_FAILURE);
535   else
536     exit(EXIT_SUCCESS);
537 }
538 
539 
540 static void
show_version()541 show_version()
542 {
543   fprintf(stdout,
544 #ifdef BUILD_GUI
545 "ttfautohintGUI " VERSION "\n"
546 #else
547 "ttfautohint " VERSION "\n"
548 #endif
549 "Copyright (C) 2011-2021 Werner Lemberg <wl@gnu.org>.\n"
550 "License: FreeType License (FTL) or GNU GPLv2.\n"
551 "This is free software: you are free to change and redistribute it.\n"
552 "There is NO WARRANTY, to the extent permitted by law.\n");
553 
554   exit(EXIT_SUCCESS);
555 }
556 #endif // CONSOLE_OUTPUT
557 
558 
559 #ifndef BUILD_GUI
560 
561 typedef const struct FT_error_
562 {
563   int err_code;
564   const char* err_msg;
565 } FT_error;
566 
567 static FT_error FT_errors[] =
568 
569 #undef __FTERRORS_H__
570 #define FT_ERRORDEF(e, v, s) { e, s },
571 #define FT_ERROR_START_LIST {
572 #define FT_ERROR_END_LIST { 0, NULL } };
573 #include FT_ERRORS_H
574 
575 
576 static const char*
577 FT_get_error_message(FT_Error error)
578 {
579   FT_error* e = FT_errors;
580 
581   while (e->err_code || e->err_msg)
582   {
583     if (e->err_code == error)
584       return e->err_msg;
585     e++;
586   }
587 
588   return NULL;
589 }
590 
591 
592 #define BUF_SIZE 0x10000
593 
594 #define TTAG_TTFA FT_MAKE_TAG('T', 'T', 'F', 'A')
595 
596 static void
display_TTFA(FILE * in)597 display_TTFA(FILE* in)
598 {
599   FT_Byte buf[BUF_SIZE];
600   FT_Byte* in_buf;
601   size_t in_len = 0;
602   size_t read_bytes;
603 
604   if (in == stdin)
605     SET_BINARY(stdin);
606 
607   in_buf = (FT_Byte*)malloc(BUF_SIZE);
608   if (!in_buf)
609   {
610     fprintf(stderr, "Can't allocate enough memory.\n");
611     exit(EXIT_FAILURE);
612   }
613 
614   while ((read_bytes = fread(buf, 1, BUF_SIZE, in)) > 0)
615   {
616     FT_Byte* in_buf_new = (FT_Byte*)realloc(in_buf, in_len + read_bytes);
617     if (!in_buf_new)
618     {
619       fprintf(stderr, "Can't reallocate enough memory.\n");
620       exit(EXIT_FAILURE);
621     }
622     else
623       in_buf = in_buf_new;
624 
625     memcpy(in_buf + in_len, buf, read_bytes);
626 
627     in_len += read_bytes;
628   }
629 
630   if (ferror(in))
631   {
632     fprintf(stderr, "Stream error while handling input font.\n");
633     exit(EXIT_FAILURE);
634   }
635 
636   FT_Library library = NULL;
637   FT_Face face = NULL;
638   FT_Error error;
639 
640   error = FT_Init_FreeType(&library);
641   if (error)
642   {
643     fprintf(stderr, "Can't initialize FreeType library:\n"
644                     "%s\n",
645                     FT_get_error_message(error));
646     exit(EXIT_FAILURE);
647   }
648 
649   // in a TTC, a `TTFA' table is part of the first subfont,
650   // thus we can simply pass 0 as the face index
651   error = FT_New_Memory_Face(library, in_buf, (FT_Long)in_len, 0, &face);
652   if (error)
653   {
654     fprintf(stderr, "Can't open input font:\n"
655                     "%s\n",
656                     FT_get_error_message(error));
657     exit(EXIT_FAILURE);
658   }
659 
660   FT_Byte* ttfa_buf = NULL;
661   FT_ULong ttfa_len = 0;
662 
663   error = FT_Load_Sfnt_Table(face, TTAG_TTFA, 0, NULL, &ttfa_len);
664   if (error)
665   {
666     fprintf(stderr, "No `TTFA' table in font.\n");
667     goto Exit;
668   }
669 
670   ttfa_buf = (FT_Byte*)malloc(ttfa_len);
671   if (!ttfa_buf)
672   {
673     fprintf(stderr, "Can't allocate enough memory.\n");
674     exit(EXIT_FAILURE);
675   }
676 
677   error = FT_Load_Sfnt_Table(face, TTAG_TTFA, 0, ttfa_buf, &ttfa_len);
678   if (error)
679   {
680     fprintf(stderr, "Error loading `TTFA' table:\n"
681                     "%s\n",
682                     FT_get_error_message(error));
683     exit(EXIT_FAILURE);
684   }
685 
686   fprintf(stdout, "%s", ttfa_buf);
687 
688 Exit:
689   FT_Done_Face(face);
690   FT_Done_FreeType(library);
691 
692   free(in_buf);
693   free(ttfa_buf);
694   if (in != stdin)
695     fclose(in);
696 
697   exit(EXIT_SUCCESS);
698 }
699 #endif
700 
701 
702 int
main(int argc,char ** argv)703 main(int argc,
704      char** argv)
705 {
706   int hinting_range_min = 0;
707   int hinting_range_max = 0;
708   int hinting_limit = 0;
709   int increase_x_height = 0;
710   int fallback_stem_width = 0;
711 
712   bool have_hinting_range_min = false;
713   bool have_hinting_range_max = false;
714   bool have_hinting_limit = false;
715   bool have_increase_x_height = false;
716   bool have_fallback_stem_width = false;
717 
718   int gray_stem_width_mode = TA_STEM_WIDTH_MODE_QUANTIZED;
719   int gdi_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_STRONG;
720   int dw_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_QUANTIZED;
721 
722 #ifndef BUILD_GUI
723   bool have_option_w = false;
724   const char* option_w_arg;
725 #endif
726 
727   bool ignore_restrictions = false;
728   bool windows_compatibility = false;
729   bool adjust_subglyphs = false;
730   bool hint_composites = false;
731   bool no_info = false;
732   bool detailed_info = false;
733   bool TTFA_info = false;
734 #ifndef BUILD_GUI
735   bool show_TTFA_info = false;
736 #endif
737   bool symbol = false;
738   bool fallback_scaling = false;
739 
740   const char* default_script = NULL;
741   bool have_default_script = false;
742   const char* fallback_script = NULL;
743   bool have_fallback_script = false;
744   const char* x_height_snapping_exceptions_string = NULL;
745   bool have_x_height_snapping_exceptions_string = false;
746   const char* family_suffix = NULL;
747   bool have_family_suffix = false;
748 
749   bool dehint = false;
750 
751 #ifndef BUILD_GUI
752   bool debug = false;
753 
754   TA_Progress_Func progress_func = NULL;
755   TA_Error_Func err_func = err;
756   TA_Info_Func info_func = info;
757   TA_Info_Post_Func info_post_func = info_post;
758 
759   const char* control_name = NULL;
760   const char* reference_name = NULL;
761   int reference_index = 0;
762 
763   unsigned long long epoch = ULLONG_MAX;
764 #endif
765 
766   // For real numbers (both parsing and displaying) we only use `.' as the
767   // decimal separator; similarly, we don't want localized formats like a
768   // thousands separator for any number.
769   setlocale(LC_NUMERIC, "C");
770 
771   // make GNU, Qt, and X11 command line options look the same;
772   // we allow `--foo=bar', `--foo bar', `-foo=bar', `-foo bar',
773   // and short options specific to ttfautohint
774 
775   // set up a new argument string
776   vector<string> new_arg_string;
777   new_arg_string.push_back(argv[0]);
778 
779   while (1)
780   {
781     // use pseudo short options for long-only options
782     enum
783     {
784       PASS_THROUGH = CHAR_MAX + 1,
785       HELP_ALL_OPTION,
786       DEBUG_OPTION
787     };
788 
789     static struct option long_options[] =
790     {
791       {"help", no_argument, NULL, 'h'},
792 #ifdef BUILD_GUI
793       {"help-all", no_argument, NULL, HELP_ALL_OPTION},
794 #endif
795 
796       // ttfautohint options
797       {"adjust-subglyphs", no_argument, NULL, 'p'},
798       {"composites", no_argument, NULL, 'c'},
799 #ifndef BUILD_GUI
800       {"control-file", required_argument, NULL, 'm'},
801       {"debug", no_argument, NULL, DEBUG_OPTION},
802 #endif
803       {"default-script", required_argument, NULL, 'D'},
804       {"dehint", no_argument, NULL, 'd'},
805       {"detailed-info", no_argument, NULL, 'I'},
806       {"fallback-scaling", no_argument, NULL, 'S'},
807       {"fallback-script", required_argument, NULL, 'f'},
808       {"fallback-stem-width", required_argument, NULL, 'H'},
809       {"family-suffix", required_argument, NULL, 'F'},
810       {"hinting-limit", required_argument, NULL, 'G'},
811       {"hinting-range-max", required_argument, NULL, 'r'},
812       {"hinting-range-min", required_argument, NULL, 'l'},
813       {"ignore-restrictions", no_argument, NULL, 'i'},
814       {"increase-x-height", required_argument, NULL, 'x'},
815       {"no-info", no_argument, NULL, 'n'},
816       {"pre-hinting", no_argument, NULL, 'p'},
817 #ifndef BUILD_GUI
818       {"reference", required_argument, NULL, 'R'},
819       {"reference-index", required_argument, NULL, 'Z'},
820 #endif
821       {"stem-width-mode", required_argument, NULL, 'a'},
822       {"strong-stem-width", required_argument, NULL, 'w'},
823       {"symbol", no_argument, NULL, 's'},
824       {"ttfa-table", no_argument, NULL, 't'},
825 #ifndef BUILD_GUI
826       {"ttfa-info", no_argument, NULL, 'T'},
827 #endif
828       {"verbose", no_argument, NULL, 'v'},
829       {"version", no_argument, NULL, 'V'},
830       {"windows-compatibility", no_argument, NULL, 'W'},
831       {"x-height-snapping-exceptions", required_argument, NULL, 'X'},
832 
833       // Qt options
834       {"graphicssystem", required_argument, NULL, PASS_THROUGH},
835       {"reverse", no_argument, NULL, PASS_THROUGH},
836       {"session", required_argument, NULL, PASS_THROUGH},
837       {"style", required_argument, NULL, PASS_THROUGH},
838       {"stylesheet", required_argument, NULL, PASS_THROUGH},
839 
840       // X11 options
841       {"background", required_argument, NULL, PASS_THROUGH},
842       {"bg", required_argument, NULL, PASS_THROUGH},
843       {"btn", required_argument, NULL, PASS_THROUGH},
844       {"button", required_argument, NULL, PASS_THROUGH},
845       {"cmap", no_argument, NULL, PASS_THROUGH},
846       {"display", required_argument, NULL, PASS_THROUGH},
847       {"fg", required_argument, NULL, PASS_THROUGH},
848       {"fn", required_argument, NULL, PASS_THROUGH},
849       {"font", required_argument, NULL, PASS_THROUGH},
850       {"foreground", required_argument, NULL, PASS_THROUGH},
851       {"geometry", required_argument, NULL, PASS_THROUGH},
852       {"im", required_argument, NULL, PASS_THROUGH},
853       {"inputstyle", required_argument, NULL, PASS_THROUGH},
854       {"name", required_argument, NULL, PASS_THROUGH},
855       {"ncols", required_argument, NULL, PASS_THROUGH},
856       {"title", required_argument, NULL, PASS_THROUGH},
857       {"visual", required_argument, NULL, PASS_THROUGH},
858 
859       {NULL, 0, NULL, 0}
860     };
861 
862     int option_index;
863     int c = getopt_long_only(argc, argv,
864 #ifdef BUILD_GUI
865                              "a:cdD:f:F:G:hH:iIl:npr:sStvVw:Wx:X:",
866 #else
867                              "a:cdD:f:F:G:hH:iIl:m:npr:R:sStTvVw:Wx:X:Z:",
868 #endif
869                              long_options, &option_index);
870     if (c == -1)
871       break;
872 
873     switch (c)
874     {
875     case 'a':
876       if (strlen(optarg) != 3)
877       {
878         fprintf(stderr, "Stem width mode string must consist of exactly"
879                         " three letters\n");
880         exit(EXIT_FAILURE);
881       }
882 
883       switch (optarg[0])
884       {
885       case 'n':
886         gray_stem_width_mode = TA_STEM_WIDTH_MODE_NATURAL;
887         break;
888       case 'q':
889         gray_stem_width_mode = TA_STEM_WIDTH_MODE_QUANTIZED;
890         break;
891       case 's':
892         gray_stem_width_mode = TA_STEM_WIDTH_MODE_STRONG;
893         break;
894       default:
895         fprintf(stderr, "Stem width mode letter for grayscale rendering"
896                         " must be `n', `q', or `s'\n");
897         exit(EXIT_FAILURE);
898       }
899 
900       switch (optarg[1])
901       {
902       case 'n':
903         gdi_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_NATURAL;
904         break;
905       case 'q':
906         gdi_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_QUANTIZED;
907         break;
908       case 's':
909         gdi_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_STRONG;
910         break;
911       default:
912         fprintf(stderr, "Stem width mode letter for GDI ClearType rendering"
913                         " must be `n', `q', or `s'\n");
914         exit(EXIT_FAILURE);
915       }
916 
917       switch (optarg[2])
918       {
919       case 'n':
920         dw_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_NATURAL;
921         break;
922       case 'q':
923         dw_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_QUANTIZED;
924         break;
925       case 's':
926         dw_cleartype_stem_width_mode = TA_STEM_WIDTH_MODE_STRONG;
927         break;
928       default:
929         fprintf(stderr, "Stem width mode letter for DW ClearType rendering"
930                         " must be `n', `q', or `s'\n");
931         exit(EXIT_FAILURE);
932       }
933 
934       break;
935 
936     case 'c':
937       hint_composites = true;
938       break;
939 
940     case 'd':
941       dehint = true;
942       break;
943 
944     case 'D':
945       default_script = optarg;
946       have_default_script = true;
947       break;
948 
949     case 'f':
950       fallback_script = optarg;
951       have_fallback_script = true;
952       break;
953 
954     case 'F':
955       family_suffix = optarg;
956       have_family_suffix = true;
957       break;
958 
959     case 'G':
960       hinting_limit = atoi(optarg);
961       have_hinting_limit = true;
962       break;
963 
964     case 'h':
965 #ifdef CONSOLE_OUTPUT
966       show_help(false, false);
967 #endif
968       break;
969 
970     case 'H':
971       fallback_stem_width = atoi(optarg);
972       have_fallback_stem_width = true;
973       break;
974 
975     case 'i':
976       ignore_restrictions = true;
977       break;
978 
979     case 'I':
980       detailed_info = true;
981       no_info = false;
982       break;
983 
984     case 'l':
985       hinting_range_min = atoi(optarg);
986       have_hinting_range_min = true;
987       break;
988 
989 #ifndef BUILD_GUI
990     case 'm':
991       control_name = optarg;
992       break;
993 #endif
994 
995     case 'n':
996       no_info = true;
997       detailed_info = false;
998       break;
999 
1000     case 'p':
1001       adjust_subglyphs = true;
1002       break;
1003 
1004     case 'r':
1005       hinting_range_max = atoi(optarg);
1006       have_hinting_range_max = true;
1007       break;
1008 
1009 #ifndef BUILD_GUI
1010     case 'R':
1011       reference_name = optarg;
1012       break;
1013 #endif
1014 
1015     case 's':
1016       symbol = true;
1017       break;
1018 
1019     case 'S':
1020       fallback_scaling = true;
1021       break;
1022 
1023     case 't':
1024       TTFA_info = true;
1025       break;
1026 
1027 #ifndef BUILD_GUI
1028     case 'T':
1029       show_TTFA_info = true;
1030       break;
1031 #endif
1032 
1033     case 'v':
1034 #ifndef BUILD_GUI
1035       progress_func = progress;
1036 #endif
1037       break;
1038 
1039     case 'V':
1040 #ifdef CONSOLE_OUTPUT
1041       show_version();
1042 #endif
1043       break;
1044 
1045     case 'w':
1046       gray_stem_width_mode = strchr(optarg, 'g')
1047                                ? TA_STEM_WIDTH_MODE_STRONG
1048                                : TA_STEM_WIDTH_MODE_QUANTIZED;
1049       gdi_cleartype_stem_width_mode = strchr(optarg, 'G')
1050                                         ? TA_STEM_WIDTH_MODE_STRONG
1051                                         : TA_STEM_WIDTH_MODE_QUANTIZED;
1052       dw_cleartype_stem_width_mode = strchr(optarg, 'D')
1053                                        ? TA_STEM_WIDTH_MODE_STRONG
1054                                        : TA_STEM_WIDTH_MODE_QUANTIZED;
1055 #ifndef BUILD_GUI
1056       have_option_w = true;
1057       option_w_arg = optarg;
1058 #endif
1059       break;
1060 
1061     case 'W':
1062       windows_compatibility = true;
1063       break;
1064 
1065     case 'x':
1066       increase_x_height = atoi(optarg);
1067       have_increase_x_height = true;
1068       break;
1069 
1070     case 'X':
1071       x_height_snapping_exceptions_string = optarg;
1072       have_x_height_snapping_exceptions_string = true;
1073       break;
1074 
1075 #ifndef BUILD_GUI
1076     case 'Z':
1077       reference_index = atoi(optarg);
1078       break;
1079 #endif
1080 
1081 #ifndef BUILD_GUI
1082     case DEBUG_OPTION:
1083       debug = true;
1084       break;
1085 #endif
1086 
1087 #ifdef BUILD_GUI
1088     case HELP_ALL_OPTION:
1089 #ifdef CONSOLE_OUTPUT
1090       show_help(true, false);
1091 #endif
1092       break;
1093 #endif
1094 
1095     case PASS_THROUGH:
1096       {
1097         // append argument with proper syntax for Qt
1098         string arg;
1099         arg += '-';
1100         arg += long_options[option_index].name;
1101 
1102         new_arg_string.push_back(arg);
1103         if (optarg)
1104           new_arg_string.push_back(optarg);
1105         break;
1106       }
1107 
1108     default:
1109       exit(EXIT_FAILURE);
1110     }
1111   }
1112 
1113   if (dehint)
1114   {
1115     // -d makes ttfautohint ignore all other hinting options
1116     have_default_script = false;
1117     have_fallback_script = false;
1118     have_fallback_stem_width = false;
1119     have_hinting_limit = false;
1120     have_hinting_range_max = false;
1121     have_hinting_range_min = false;
1122     have_increase_x_height = false;
1123     have_x_height_snapping_exceptions_string = false;
1124   }
1125 
1126 #ifndef BUILD_GUI
1127   if (show_TTFA_info)
1128   {
1129     // -T makes ttfautohint ignore even more options
1130     have_default_script = false;
1131     have_fallback_script = false;
1132     have_fallback_stem_width = false;
1133     have_family_suffix = false;
1134     have_hinting_limit = false;
1135     have_hinting_range_max = false;
1136     have_hinting_range_min = false;
1137     have_increase_x_height = false;
1138     have_x_height_snapping_exceptions_string = false;
1139     debug = false;
1140   }
1141 #endif
1142 
1143   if (!have_default_script)
1144     default_script = "latn";
1145   if (!have_fallback_script)
1146     fallback_script = "none";
1147   if (!have_hinting_range_min)
1148     hinting_range_min = TA_HINTING_RANGE_MIN;
1149   if (!have_hinting_range_max)
1150     hinting_range_max = TA_HINTING_RANGE_MAX;
1151   if (!have_hinting_limit)
1152     hinting_limit = TA_HINTING_LIMIT;
1153   if (!have_increase_x_height)
1154     increase_x_height = TA_INCREASE_X_HEIGHT;
1155   if (!have_x_height_snapping_exceptions_string)
1156     x_height_snapping_exceptions_string = "";
1157   if (!have_fallback_stem_width)
1158     fallback_stem_width = 0; // redundant, but avoids a compiler warning
1159   if (!have_family_suffix)
1160     family_suffix = "";
1161 
1162 #ifndef BUILD_GUI
1163 
1164   // check SOURE_DATE_EPOCH environment variable
1165   const char* source_date_epoch = getenv("SOURCE_DATE_EPOCH");
1166   if (source_date_epoch)
1167   {
1168     char* endptr;
1169     errno = 0;
1170 
1171     epoch = strtoull(source_date_epoch, &endptr, 10);
1172     if ((errno == ERANGE && (epoch == ULLONG_MAX
1173                              || epoch == 0))
1174         || (errno != 0
1175             && epoch == 0))
1176     {
1177       fprintf(stderr,
1178               "Environment variable `SOURCE_DATE_EPOCH' ignored:\n"
1179               "  strtoull: %s\n",
1180               strerror(errno));
1181       epoch = ULLONG_MAX;
1182     }
1183     else if (endptr == source_date_epoch)
1184     {
1185       fprintf(stderr,
1186               "Environment variable `SOURCE_DATE_EPOCH' ignored:\n"
1187               " No digits were found: %s\n",
1188               endptr);
1189       epoch = ULLONG_MAX;
1190     }
1191     else if (*endptr != '\0')
1192     {
1193       fprintf(stderr,
1194               "Environment variable `SOURCE_DATE_EPOCH' ignored:\n"
1195               " Trailing garbage: %s\n",
1196               endptr);
1197       epoch = ULLONG_MAX;
1198     }
1199     else if (epoch > ULONG_MAX)
1200     {
1201       fprintf(stderr,
1202               "Environment variable `SOURCE_DATE_EPOCH' ignored\n"
1203               " value must be smaller than or equal to %lu\n"
1204               " but was found to be %llu\n",
1205               ULONG_MAX, epoch);
1206       epoch = ULLONG_MAX;
1207     }
1208   }
1209 
1210   if (have_option_w)
1211   {
1212     char option_a_arg[4];
1213 
1214     option_a_arg[0] =
1215       gray_stem_width_mode == TA_STEM_WIDTH_MODE_STRONG ? 's' : 'q';
1216     option_a_arg[1] =
1217       gdi_cleartype_stem_width_mode == TA_STEM_WIDTH_MODE_STRONG ? 's' : 'q';
1218     option_a_arg[2] =
1219       dw_cleartype_stem_width_mode == TA_STEM_WIDTH_MODE_STRONG ? 's' : 'q';
1220     option_a_arg[3] = '\0';
1221 
1222     fprintf(stderr,
1223             "Warning: Option `-w %s' is deprecated!"
1224             "  Use option `-a %s' instead\n",
1225             option_w_arg,
1226             option_a_arg);
1227   }
1228 
1229   if (!isatty(fileno(stderr)) && !debug)
1230     setvbuf(stderr, (char*)NULL, _IONBF, BUFSIZ);
1231 
1232   if (hinting_range_min < 2)
1233   {
1234     fprintf(stderr, "The hinting range minimum must be at least 2\n");
1235     exit(EXIT_FAILURE);
1236   }
1237   if (hinting_range_max < hinting_range_min)
1238   {
1239     fprintf(stderr, "The hinting range maximum must not be smaller"
1240                     " than the minimum (%d)\n",
1241                     hinting_range_min);
1242     exit(EXIT_FAILURE);
1243   }
1244   if (hinting_limit != 0 && hinting_limit < hinting_range_max)
1245   {
1246     fprintf(stderr, "A non-zero hinting limit must not be smaller"
1247                     " than the hinting range maximum (%d)\n",
1248                     hinting_range_max);
1249     exit(EXIT_FAILURE);
1250   }
1251   if (increase_x_height != 0 && increase_x_height < 6)
1252   {
1253     fprintf(stderr, "A non-zero x height increase limit"
1254                     " must be larger than or equal to 6\n");
1255     exit(EXIT_FAILURE);
1256   }
1257   if (have_fallback_stem_width && fallback_stem_width <= 0)
1258   {
1259     fprintf(stderr, "The fallback stem width"
1260                     " must be a positive integer\n");
1261     exit(EXIT_FAILURE);
1262   }
1263 
1264   if (have_default_script)
1265   {
1266     const Tag_Names* sn;
1267 
1268     for (sn = script_names; sn->tag; sn++)
1269       if (!strcmp(default_script, sn->tag))
1270         break;
1271     if (!sn->tag)
1272     {
1273       fprintf(stderr, "Unknown script tag `%s'\n", default_script);
1274       exit(EXIT_FAILURE);
1275     }
1276   }
1277 
1278   if (have_fallback_script)
1279   {
1280     const Tag_Names* sn;
1281 
1282     for (sn = script_names; sn->tag; sn++)
1283       if (!strcmp(fallback_script, sn->tag))
1284         break;
1285     if (!sn->tag)
1286     {
1287       fprintf(stderr, "Unknown script tag `%s'\n", fallback_script);
1288       exit(EXIT_FAILURE);
1289     }
1290   }
1291 
1292   if (symbol
1293       && have_fallback_stem_width
1294       && fallback_scaling)
1295     fprintf(stderr,
1296             "Warning: Setting a fallback stem width for a symbol font\n"
1297             "         with fallback scaling only has no effect\n");
1298 
1299   if (const char* pos = check_family_suffix(family_suffix))
1300   {
1301     fprintf(stderr,
1302             "Invalid character in family suffix:\n"
1303             "  %s\n"
1304             "  %*s\n",
1305             family_suffix,
1306             int(pos - family_suffix + 1), "^");
1307     exit(EXIT_FAILURE);
1308   }
1309 
1310   int num_args = argc - optind;
1311 
1312   if (num_args > 2)
1313     show_help(false, true);
1314 
1315   FILE* in;
1316   if (num_args > 0)
1317   {
1318     in = fopen(argv[optind], "rb");
1319     if (!in)
1320     {
1321       fprintf(stderr,
1322               "The following error occurred while opening font `%s':\n"
1323               "\n"
1324               "  %s\n",
1325               argv[optind], strerror(errno));
1326       exit(EXIT_FAILURE);
1327     }
1328   }
1329   else
1330   {
1331     if (isatty(fileno(stdin)))
1332       show_help(false, true);
1333     in = stdin;
1334   }
1335 
1336   if (show_TTFA_info)
1337     display_TTFA(in); // this function doesn't return
1338 
1339   FILE* out;
1340   if (num_args > 1)
1341   {
1342     if (!strcmp(argv[optind], argv[optind + 1]))
1343     {
1344       fprintf(stderr, "Input and output file names must not be identical\n");
1345       exit(EXIT_FAILURE);
1346     }
1347 
1348     out = fopen(argv[optind + 1], "wb");
1349     if (!out)
1350     {
1351       fprintf(stderr,
1352               "The following error occurred while opening font `%s':\n"
1353               "\n"
1354               "  %s\n",
1355               argv[optind + 1], strerror(errno));
1356       exit(EXIT_FAILURE);
1357     }
1358   }
1359   else
1360   {
1361     if (isatty(fileno(stdout)))
1362       show_help(false, true);
1363     out = stdout;
1364   }
1365 
1366   FILE* control = NULL;
1367   if (control_name)
1368   {
1369     control = fopen(control_name, "r");
1370     if (!control)
1371     {
1372       fprintf(stderr,
1373               "The following error occurred"
1374                 " while opening control file `%s':\n"
1375               "\n"
1376               "  %s\n",
1377               control_name, strerror(errno));
1378       exit(EXIT_FAILURE);
1379     }
1380   }
1381   else
1382     control = NULL;
1383 
1384   FILE* reference = NULL;
1385   if (reference_name)
1386   {
1387     reference = fopen(reference_name, "r");
1388     if (!reference)
1389     {
1390       fprintf(stderr,
1391               "The following error occurred"
1392                 " while opening reference font `%s':\n"
1393               "\n"
1394               "  %s\n",
1395               reference_name, strerror(errno));
1396       exit(EXIT_FAILURE);
1397     }
1398 
1399     if (reference_index != 0)
1400     {
1401       FT_Library library = NULL;
1402       FT_Face face = NULL;
1403       FT_Error error;
1404 
1405       error = FT_Init_FreeType(&library);
1406       if (error)
1407       {
1408         fprintf(stderr, "Can't initialize FreeType library:\n"
1409                         "%s\n",
1410                         FT_get_error_message(error));
1411         exit(EXIT_FAILURE);
1412       }
1413 
1414       error = FT_New_Face(library, reference_name, -1, &face);
1415       if (error)
1416       {
1417         fprintf(stderr, "Can't check number of faces in reference font:\n"
1418                         "%s\n",
1419                         FT_get_error_message(error));
1420         exit(EXIT_FAILURE);
1421       }
1422 
1423       if (reference_index < 0
1424           || reference_index >= face->num_faces)
1425       {
1426         fprintf(stderr, "Face index for reference font must be"
1427                           " in the range [0;%ld]\n",
1428                         face->num_faces - 1);
1429         exit(EXIT_FAILURE);
1430       }
1431 
1432       FT_Done_Face(face);
1433       FT_Done_FreeType(library);
1434     }
1435   }
1436   else
1437     reference = NULL;
1438 
1439   Progress_Data progress_data = {-1, 1, 0};
1440   Error_Data error_data = {control_name};
1441   Info_Data info_data;
1442 
1443   if (!*family_suffix)
1444     info_post_func = NULL;
1445 
1446   info_data.no_info = no_info;
1447   info_data.detailed_info = detailed_info;
1448   info_data.info_string = NULL; // must be deallocated after use
1449   info_data.info_string_wide = NULL; // must be deallocated after use
1450   info_data.info_string_len = 0;
1451   info_data.info_string_wide_len = 0;
1452 
1453   info_data.control_name = control_name;
1454   info_data.reference_name = reference_name;
1455   info_data.reference_index = reference_index;
1456 
1457   info_data.hinting_range_min = hinting_range_min;
1458   info_data.hinting_range_max = hinting_range_max;
1459   info_data.hinting_limit = hinting_limit;
1460 
1461   info_data.gray_stem_width_mode = gray_stem_width_mode;
1462   info_data.gdi_cleartype_stem_width_mode = gdi_cleartype_stem_width_mode;
1463   info_data.dw_cleartype_stem_width_mode = dw_cleartype_stem_width_mode;
1464 
1465   info_data.windows_compatibility = windows_compatibility;
1466   info_data.adjust_subglyphs = adjust_subglyphs;
1467   info_data.hint_composites = hint_composites;
1468   info_data.increase_x_height = increase_x_height;
1469   info_data.x_height_snapping_exceptions_string = x_height_snapping_exceptions_string;
1470   info_data.family_suffix = family_suffix;
1471   info_data.family_data_head = NULL;
1472   info_data.fallback_stem_width = fallback_stem_width;
1473   info_data.symbol = symbol;
1474   info_data.fallback_scaling = fallback_scaling;
1475   info_data.TTFA_info = TTFA_info;
1476 
1477   strncpy(info_data.default_script,
1478           default_script,
1479           sizeof (info_data.default_script));
1480   strncpy(info_data.fallback_script,
1481           fallback_script,
1482           sizeof (info_data.fallback_script));
1483 
1484   info_data.dehint = dehint;
1485 
1486   if (!no_info)
1487   {
1488     int ret = build_version_string(&info_data);
1489     if (ret == 1)
1490       fprintf(stderr, "Warning: Can't allocate memory"
1491                       " for ttfautohint options string in `name' table\n");
1492     else if (ret == 2)
1493       fprintf(stderr, "Warning: ttfautohint options string"
1494                       " in `name' table too long\n");
1495   }
1496 
1497   if (in == stdin)
1498     SET_BINARY(stdin);
1499   if (out == stdout)
1500     SET_BINARY(stdout);
1501 
1502   TA_Error error =
1503     TTF_autohint("in-file, out-file, control-file,"
1504                  "reference-file, reference-index, reference-name,"
1505                  "hinting-range-min, hinting-range-max, hinting-limit,"
1506                  "gray-stem-width-mode, gdi-cleartype-stem-width-mode,"
1507                  "dw-cleartype-stem-width-mode,"
1508                  "progress-callback, progress-callback-data,"
1509                  "error-callback, error-callback-data,"
1510                  "info-callback, info-post-callback, info-callback-data,"
1511                  "ignore-restrictions, windows-compatibility,"
1512                  "adjust-subglyphs, hint-composites,"
1513                  "increase-x-height, x-height-snapping-exceptions,"
1514                  "fallback-stem-width, default-script,"
1515                  "fallback-script, fallback-scaling,"
1516                  "symbol, dehint, debug, TTFA-info, epoch",
1517                  in, out, control,
1518                  reference, reference_index, reference_name,
1519                  hinting_range_min, hinting_range_max, hinting_limit,
1520                  gray_stem_width_mode, gdi_cleartype_stem_width_mode,
1521                  dw_cleartype_stem_width_mode,
1522                  progress_func, &progress_data,
1523                  err_func, &error_data,
1524                  info_func, info_post_func, &info_data,
1525                  ignore_restrictions, windows_compatibility,
1526                  adjust_subglyphs, hint_composites,
1527                  increase_x_height, x_height_snapping_exceptions_string,
1528                  fallback_stem_width, default_script,
1529                  fallback_script, fallback_scaling,
1530                  symbol, dehint, debug, TTFA_info, epoch);
1531 
1532   if (!no_info)
1533   {
1534     free(info_data.info_string);
1535     free(info_data.info_string_wide);
1536   }
1537 
1538   if (in != stdin)
1539     fclose(in);
1540   if (out != stdout)
1541     fclose(out);
1542   if (control)
1543     fclose(control);
1544   if (reference)
1545     fclose(reference);
1546 
1547   exit(error ? EXIT_FAILURE : EXIT_SUCCESS);
1548 
1549   return 0; // never reached
1550 
1551 #else // BUILD_GUI
1552 
1553   int new_argc = (int)new_arg_string.size();
1554   char** new_argv = new char*[new_argc];
1555 
1556   // construct new argc and argv variables from collected data
1557   for (unsigned int i = 0; i < (unsigned int)new_argc; i++)
1558     new_argv[i] = const_cast<char*>(new_arg_string[i].data());
1559 
1560   QApplication app(new_argc, new_argv);
1561   app.setApplicationName("TTFautohint");
1562   app.setApplicationVersion(VERSION);
1563   app.setOrganizationName("FreeType");
1564   app.setOrganizationDomain("freetype.org");
1565 
1566   bool alternative_layout = false;
1567   {
1568     // Display the window off the screen -- to get proper window dimensions
1569     // including the frame, the window manager must have a chance to
1570     // decorate it.
1571     //
1572     // We don't want to change the default window positioning algorithm of
1573     // the platform's window manager, so we create the main GUI window
1574     // twice.
1575     //
1576     // The original idea, however, was to simply move the off-screen window
1577     // back to the screen with
1578     //
1579     //   gui.move(100, 100);
1580     //   gui.setAttribute(Qt::WA_Moved, false);
1581     //   gui.show();
1582     //
1583     // (unsetting the `WA_Moved' attribute makes the window manager handle
1584     // the previous call to `move' as a position suggestion instead of a
1585     // request).  Unfortuntely, there seems to be a bug in Qt 4.8.4 which
1586     // prevents any effect of unsetting `WA_Moved' if `show' has already
1587     // been called.
1588 
1589     Main_GUI dummy(alternative_layout,
1590                    hinting_range_min, hinting_range_max, hinting_limit,
1591                    gray_stem_width_mode, gdi_cleartype_stem_width_mode,
1592                    dw_cleartype_stem_width_mode, increase_x_height,
1593                    x_height_snapping_exceptions_string, fallback_stem_width,
1594                    ignore_restrictions, windows_compatibility, adjust_subglyphs,
1595                    hint_composites, no_info, detailed_info,
1596                    default_script, fallback_script, fallback_scaling,
1597                    family_suffix, symbol, dehint, TTFA_info);
1598 
1599     dummy.move(-50000, -50000);
1600     dummy.show();
1601 
1602     // if the vertical size of our window is too large,
1603     // select a horizontal layout
1604     QRect screen(QApplication::desktop()->availableGeometry());
1605     if (dummy.frameGeometry().height() > screen.height())
1606       alternative_layout = true;
1607   }
1608 
1609   Main_GUI gui(alternative_layout,
1610                hinting_range_min, hinting_range_max, hinting_limit,
1611                gray_stem_width_mode, gdi_cleartype_stem_width_mode,
1612                dw_cleartype_stem_width_mode, increase_x_height,
1613                x_height_snapping_exceptions_string, fallback_stem_width,
1614                ignore_restrictions, windows_compatibility, adjust_subglyphs,
1615                hint_composites, no_info, detailed_info,
1616                default_script, fallback_script, fallback_scaling,
1617                family_suffix, symbol, dehint, TTFA_info);
1618   gui.show();
1619 
1620   return app.exec();
1621 
1622 #endif // BUILD_GUI
1623 }
1624 
1625 // end of main.cpp
1626