Add option `--fallback-scaling'/`-S' to front-ends.
[ttfautohint.git] / frontend / main.cpp
blobc28c944d0171c3a6eae7f4978bd5be72a4e401c3
1 // main.cpp
3 // Copyright (C) 2011-2016 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.
14 // This program is a wrapper for `TTF_autohint'.
16 #ifdef BUILD_GUI
17 # ifndef _WIN32
18 # define CONSOLE_OUTPUT
19 # endif
20 #else
21 # define CONSOLE_OUTPUT
22 #endif
24 #include <config.h>
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>
35 #include <vector>
36 #include <string>
38 #if 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
48 #include <ttfautohint.h>
49 #include <numberset.h>
52 #ifdef _WIN32
53 # include <fcntl.h>
54 # define SET_BINARY(f) do { \
55 if (!isatty(fileno(f))) \
56 setmode(fileno(f), O_BINARY); \
57 } while (0)
58 #endif
60 #ifndef SET_BINARY
61 # define SET_BINARY(f) do {} while (0)
62 #endif
65 using namespace std;
68 typedef struct Tag_Names_
70 const char* tag;
71 const char* description;
72 } Tag_Names;
75 // the available script tags and its descriptions are directly extracted
76 // from `ttfautohint-scripts.h'
77 #undef SCRIPT
78 #define SCRIPT(s, S, d, h, H, ss) \
79 {#s, d},
81 const Tag_Names script_names[] =
83 #include <ttfautohint-scripts.h>
84 {NULL, NULL}
88 // the available feature tags and its descriptions are directly extracted
89 // from `ttfautohint-coverages.h'
90 #undef COVERAGE
91 #define COVERAGE(n, N, d, t, t1, t2, t3, t4) \
92 {#t, d},
94 const Tag_Names feature_names[] =
96 #include <ttfautohint-coverages.h>
97 {NULL, NULL}
101 #ifndef BUILD_GUI
102 extern "C" {
104 typedef struct Progress_Data_
106 long last_sfnt;
107 bool begin;
108 int last_percent;
109 } Progress_Data;
112 static int
113 progress(long curr_idx,
114 long num_glyphs,
115 long curr_sfnt,
116 long num_sfnts,
117 void* user)
119 Progress_Data* data = (Progress_Data*)user;
121 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
123 fprintf(stderr, "subfont %ld of %ld\n", curr_sfnt + 1, num_sfnts);
124 data->last_sfnt = curr_sfnt;
125 data->last_percent = 0;
126 data->begin = true;
129 if (data->begin)
131 fprintf(stderr, " %ld glyphs\n"
132 " ", num_glyphs);
133 data->begin = false;
136 // print progress approx. every 10%
137 int curr_percent = curr_idx * 100 / num_glyphs;
138 int curr_diff = curr_percent - data->last_percent;
140 if (curr_diff >= 10)
142 fprintf(stderr, " %d%%", curr_percent);
143 data->last_percent = curr_percent - curr_percent % 10;
146 if (curr_idx + 1 == num_glyphs)
147 fprintf(stderr, "\n");
149 return 0;
153 typedef struct Error_Data_
155 const char* control_name;
156 } Error_Data;
159 static void
160 err(TA_Error error,
161 const char* error_string,
162 unsigned int errlinenum,
163 const char* errline,
164 const char* errpos,
165 void* user)
167 Error_Data* data = static_cast<Error_Data*>(user);
169 if (!error)
170 return;
172 // We replace some terse error strings with more user-friendly versions.
173 if (error == TA_Err_Invalid_FreeType_Version)
174 fprintf(stderr,
175 "FreeType version 2.4.5 or higher is needed.\n"
176 "Perhaps using a wrong FreeType DLL?\n");
177 else if (error == TA_Err_Invalid_Font_Type)
178 fprintf(stderr,
179 "This font is not a valid font"
180 " in SFNT format with TrueType outlines.\n"
181 "In particular, CFF outlines are not supported.\n");
182 else if (error == TA_Err_Already_Processed)
183 fprintf(stderr,
184 "This font has already been processed with ttfautohint.\n");
185 else if (error == TA_Err_Missing_Legal_Permission)
186 fprintf(stderr,
187 "Bit 1 in the `fsType' field of the `OS/2' table is set:\n"
188 "This font must not be modified"
189 " without permission of the legal owner.\n"
190 "Use command line option `-i' to continue"
191 " if you have such a permission.\n");
192 else if (error == TA_Err_Missing_Unicode_CMap)
193 fprintf(stderr,
194 "No Unicode character map.\n");
195 else if (error == TA_Err_Missing_Symbol_CMap)
196 fprintf(stderr,
197 "No symbol character map.\n");
198 else if (error == TA_Err_Missing_Glyph)
199 fprintf(stderr,
200 "No glyph for a standard character"
201 " to derive standard width and height.\n"
202 "Please check the documentation for a list of"
203 " script-specific standard characters,\n"
204 "or use option `--symbol'.\n");
205 else
207 if (error < 0x100)
208 fprintf(stderr, "An error with code 0x%02x occurred"
209 " while autohinting fonts\n",
210 error);
211 else if (error >= 0x100 && error < 0x200)
213 fprintf(stderr, "An error with code 0x%03x occurred"
214 " while parsing the argument of option `-X'",
215 error);
216 fprintf(stderr, errline ? ":\n" : ".\n");
218 if (errline)
219 fprintf(stderr, " %s\n", errline);
220 if (errpos && errline)
221 fprintf(stderr, " %*s\n", int(errpos - errline + 1), "^");
223 else if (error >= 0x200 && error < 0x300)
225 fprintf(stderr, "%s:", data->control_name);
226 if (errlinenum)
227 fprintf(stderr, "%d:", errlinenum);
228 if (errpos && errline)
229 fprintf(stderr, "%d:", int(errpos - errline + 1));
230 if (error_string)
231 fprintf(stderr, " %s", error_string);
232 fprintf(stderr, " (0x%02X)\n", error);
233 if (errline)
234 fprintf(stderr, " %s\n", errline);
235 if (errpos && errline)
236 fprintf(stderr, " %*s\n", int(errpos - errline + 1), "^");
242 } // extern "C"
243 #endif // !BUILD_GUI
246 #ifdef CONSOLE_OUTPUT
247 static void
248 show_help(bool
249 #ifdef BUILD_GUI
251 #endif
253 bool is_error)
255 FILE* handle = is_error ? stderr : stdout;
257 fprintf(handle,
258 #ifdef BUILD_GUI
259 "Usage: ttfautohintGUI [OPTION]...\n"
260 "A GUI application to replace hints in a TrueType font.\n"
261 #else
262 "Usage: ttfautohint [OPTION]... [IN-FILE [OUT-FILE]]\n"
263 "Replace hints in TrueType font IN-FILE and write output to OUT-FILE.\n"
264 "If OUT-FILE is missing, standard output is used instead;\n"
265 "if IN-FILE is missing also, standard input and output are used.\n"
266 #endif
267 "\n"
268 "The new hints are based on FreeType's auto-hinter.\n"
269 "\n"
270 "This program is a simple front-end to the `ttfautohint' library.\n"
271 "\n");
273 fprintf(handle,
274 "Long options can be given with one or two dashes,\n"
275 "and with and without equal sign between option and argument.\n"
276 "This means that the following forms are acceptable:\n"
277 "`-foo=bar', `--foo=bar', `-foo bar', `--foo bar'.\n"
278 "\n"
279 "Mandatory arguments to long options are mandatory for short options too.\n"
280 #ifdef BUILD_GUI
281 "Options not related to Qt or X11 set default values.\n"
282 #endif
283 "\n"
286 fprintf(handle,
287 "Options:\n"
288 #ifndef BUILD_GUI
289 " --debug print debugging information\n"
290 #endif
291 " -c, --composites hint glyph composites also\n"
292 " -d, --dehint remove all hints\n"
293 " -D, --default-script=S set default OpenType script (default: latn)\n"
294 " -f, --fallback-script=S set fallback script (default: none)\n"
295 " -F, --family-suffix=S append suffix to the family name string(s)\n"
296 " in the `name' table\n"
297 " -G, --hinting-limit=N switch off hinting above this PPEM value\n"
298 " (default: %d); value 0 means no limit\n"
299 " -h, --help display this help and exit\n"
300 " -H, --fallback-stem-width=N\n"
301 " set fallback stem width\n"
302 " (default: 50 font units at 2048 UPEM)\n"
303 #ifdef BUILD_GUI
304 " --help-all show Qt and X11 specific options also\n"
305 #endif
306 " -i, --ignore-restrictions override font license restrictions\n"
307 " -I, --detailed-info add detailed ttfautohint info\n"
308 " to the version string(s) in the `name' table\n"
309 " -l, --hinting-range-min=N the minimum PPEM value for hint sets\n"
310 " (default: %d)\n"
311 #ifndef BUILD_GUI
312 " -m, --control-file=FILE get control instructions from FILE\n"
313 #endif
314 " -n, --no-info don't add ttfautohint info\n"
315 " to the version string(s) in the `name' table\n"
316 " -p, --adjust-subglyphs handle subglyph adjustments in exotic fonts\n",
317 TA_HINTING_LIMIT, TA_HINTING_RANGE_MIN);
318 fprintf(handle,
319 " -r, --hinting-range-max=N the maximum PPEM value for hint sets\n"
320 " (default: %d)\n"
321 " -s, --symbol input is symbol font\n"
322 " -S, --fallback-scaling use fallback scaling, not hinting\n"
323 " -t, --ttfa-table add TTFA information table\n"
324 #ifndef BUILD_GUI
325 " -T, --ttfa-info display TTFA table in IN-FILE and exit\n"
326 #endif
327 " -v, --verbose show progress information\n"
328 " -V, --version print version information and exit\n"
329 " -w, --strong-stem-width=S use strong stem width routine for modes S,\n"
330 " where S is a string of up to three letters\n"
331 " with possible values `g' for grayscale,\n"
332 " `G' for GDI ClearType, and `D' for\n"
333 " DirectWrite ClearType (default: G)\n"
334 " -W, --windows-compatibility\n"
335 " add blue zones for `usWinAscent' and\n"
336 " `usWinDescent' to avoid clipping\n"
337 " -x, --increase-x-height=N increase x height for sizes in the range\n"
338 " 6<=PPEM<=N; value 0 switches off this feature\n"
339 " (default: %d)\n"
340 " -X, --x-height-snapping-exceptions=STRING\n"
341 " specify a comma-separated list of\n"
342 " x-height snapping exceptions, for example\n"
343 " \"-9, 13-17, 19\" (default: \"\")\n"
344 "\n",
345 TA_HINTING_RANGE_MAX, TA_INCREASE_X_HEIGHT);
347 #ifdef BUILD_GUI
348 if (all)
350 fprintf(handle,
351 "Qt Options:\n"
352 " --graphicssystem=SYSTEM\n"
353 " select a different graphics system backend\n"
354 " instead of the default one\n"
355 " (possible values: `raster', `opengl')\n"
356 " --reverse set layout direction to right-to-left\n");
357 fprintf(handle,
358 " --session=ID restore the application for the given ID\n"
359 " --style=STYLE set application GUI style\n"
360 " (possible values: motif, windows, platinum)\n"
361 " --stylesheet=SHEET apply the given Qt stylesheet\n"
362 " to the application widgets\n"
363 "\n");
365 fprintf(handle,
366 "X11 options:\n"
367 " --background=COLOR set the default background color\n"
368 " and an application palette\n"
369 " (light and dark shades are calculated)\n"
370 " --bg=COLOR same as --background\n"
371 " --btn=COLOR set the default button color\n"
372 " --button=COLOR same as --btn\n"
373 " --cmap use a private color map on an 8-bit display\n"
374 " --display=NAME use the given X-server display\n");
375 fprintf(handle,
376 " --fg=COLOR set the default foreground color\n"
377 " --fn=FONTNAME set the application font\n"
378 " --font=FONTNAME same as --fn\n"
379 " --foreground=COLOR same as --fg\n"
380 " --geometry=GEOMETRY set the client geometry of first window\n"
381 " --im=SERVER set the X Input Method (XIM) server\n"
382 " --inputstyle=STYLE set X Input Method input style\n"
383 " (possible values: onthespot, overthespot,\n"
384 " offthespot, root)\n");
385 fprintf(handle,
386 " --name=NAME set the application name\n"
387 " --ncols=COUNT limit the number of colors allocated\n"
388 " in the color cube on an 8-bit display,\n"
389 " if the application is using the\n"
390 " QApplication::ManyColor color specification\n"
391 " --title=TITLE set the application title (caption)\n"
392 " --visual=VISUAL force the application\n"
393 " to use the given visual on an 8-bit display\n"
394 " (only possible value: TrueColor)\n"
395 "\n");
397 #endif // BUILD_GUI
399 fprintf(handle,
400 "The program accepts both TTF and TTC files as input.\n"
401 "Use option -i only if you have a legal permission to modify the font.\n"
402 "The used PPEM value for option -p is FUnits per em, normally 2048.\n"
403 "With option -s, use default values for standard stem width and height,\n"
404 "otherwise they are derived from script-specific characters\n"
405 "resembling the shape of character `o'.\n"
406 "\n");
407 fprintf(handle,
408 "A hint set contains the optimal hinting for a certain PPEM value;\n"
409 "the larger the hint set range (as given by options -l and -r),\n"
410 "the more hint sets get computed, usually increasing the output font size.\n"
411 "The `gasp' table of the output file always enables grayscale hinting\n"
412 "for all sizes (limited by option -G, which is handled in the bytecode).\n"
413 "Increasing the value of -G does not increase the output font size.\n"
414 "\n");
415 fprintf(handle,
416 "Options -f and -D take a four-letter string that identifies a script.\n"
417 "Option -f sets the script used as a fallback for glyphs that can't be\n"
418 "associated with a known script. By default, such glyphs are hinted;\n"
419 "if option -S is set, they are scaled only instead. Option -D sets the\n"
420 "default script for handling OpenType features.\n"
421 "\n"
422 "Possible four-letter string values are\n"
423 "\n");
424 const Tag_Names* sn = script_names;
425 for(;;)
427 fprintf(handle, " %s (%s)",
428 sn->tag, sn->description);
429 sn++;
430 if (sn->tag)
431 fprintf(handle, ",\n");
432 else
434 fprintf(handle, ".\n");
435 break;
438 fprintf(handle,
439 #ifndef BUILD_GUI
440 "\n"
441 "A control instructions file contains entries of the form\n"
442 "\n"
443 " [<font idx>] <script> <feature> @ <glyph ids>\n"
444 "\n"
445 " [<font idx>] <glyph id> l|r <points> [(<left offset>,<right offset>)]\n"
446 "\n"
447 " [<font idx>] <glyph id> n <points>\n"
448 "\n"
449 " [<font idx>] <glyph id> t|p <points> [x <shift>] [y <shift>] @ <ppems>\n"
450 "\n"
451 "<font idx> is the current subfont, <glyph id> is a glyph name or index,\n"
452 "<glyph ids> is a set of <glyph id>s, <shift> is a real number in px,\n"
453 "<points> and <ppems> are integer ranges as with option `-X'.\n"
454 "\n"
455 "<script> and <feature> are four-letter tags that define a style\n"
456 "the <glyph ids> are assigned to; possible values for <script> are the same\n"
457 "as with option -D, possible values for <feature> are\n"
458 "\n");
459 const Tag_Names* fn = feature_names;
460 for(;;)
462 fprintf(handle, " %s (%s)",
463 fn->tag, fn->description);
464 fn++;
465 if (fn->tag)
466 fprintf(handle, ",\n");
467 else
469 fprintf(handle, ".\n");
470 break;
473 fprintf(handle,
474 "\n"
475 "`l' (`r') creates one-point segments with direction left (right).\n"
476 "<left offset> and <right offset> specify offsets (in font units)\n"
477 "relative to the corresponding points to give the segments a length.\n"
478 "`n' removes points from horizontal segments, making them `weak' points.\n"
479 "`t' (`p') applies delta exceptions to the given points before (after) IUP.\n"
480 "\n"
481 "`#' starts a line comment, which gets ignored.\n"
482 "Empty lines are ignored, too.\n"
483 "\n"
484 "Key letters `l', `r', `n', `p', `t', `x', and `y' have the verbose aliases\n"
485 "`left', `right', `nodir', `point', `touch', `xshift', and `yshift'.\n"
486 #endif
487 "\n"
488 #ifdef BUILD_GUI
489 "A command-line version of this program is called `ttfautohint'.\n"
490 #else
491 "A GUI version of this program is called `ttfautohintGUI'.\n"
492 #endif
493 "\n"
494 "Report bugs to: freetype-devel@nongnu.org\n"
495 "\n"
496 "ttfautohint home page: <http://www.freetype.org/ttfautohint>\n");
498 if (is_error)
499 exit(EXIT_FAILURE);
500 else
501 exit(EXIT_SUCCESS);
505 static void
506 show_version()
508 fprintf(stdout,
509 #ifdef BUILD_GUI
510 "ttfautohintGUI " VERSION "\n"
511 #else
512 "ttfautohint " VERSION "\n"
513 #endif
514 "Copyright (C) 2011-2016 Werner Lemberg <wl@gnu.org>.\n"
515 "License: FreeType License (FTL) or GNU GPLv2.\n"
516 "This is free software: you are free to change and redistribute it.\n"
517 "There is NO WARRANTY, to the extent permitted by law.\n");
519 exit(EXIT_SUCCESS);
521 #endif // CONSOLE_OUTPUT
524 #ifndef BUILD_GUI
526 typedef const struct FT_error_
528 int err_code;
529 const char* err_msg;
530 } FT_error;
532 static FT_error FT_errors[] =
534 #undef __FTERRORS_H__
535 #define FT_ERRORDEF(e, v, s) { e, s },
536 #define FT_ERROR_START_LIST {
537 #define FT_ERROR_END_LIST { 0, NULL } };
538 #include FT_ERRORS_H
541 static const char*
542 FT_get_error_message(FT_Error error)
544 FT_error* e = FT_errors;
546 while (e->err_code || e->err_msg)
548 if (e->err_code == error)
549 return e->err_msg;
550 e++;
553 return NULL;
557 #define BUF_SIZE 0x10000
559 #define TTAG_TTFA FT_MAKE_TAG('T', 'T', 'F', 'A')
561 static void
562 display_TTFA(FILE* in)
564 FT_Byte buf[BUF_SIZE];
565 FT_Byte* in_buf;
566 size_t in_len = 0;
567 size_t read_bytes;
569 if (in == stdin)
570 SET_BINARY(stdin);
572 in_buf = (FT_Byte*)malloc(BUF_SIZE);
573 if (!in_buf)
575 fprintf(stderr, "Can't allocate enough memory.\n");
576 exit(EXIT_FAILURE);
579 while ((read_bytes = fread(buf, 1, BUF_SIZE, in)) > 0)
581 FT_Byte* in_buf_new = (FT_Byte*)realloc(in_buf, in_len + read_bytes);
582 if (!in_buf_new)
584 fprintf(stderr, "Can't reallocate enough memory.\n");
585 exit(EXIT_FAILURE);
587 else
588 in_buf = in_buf_new;
590 memcpy(in_buf + in_len, buf, read_bytes);
592 in_len += read_bytes;
595 if (ferror(in))
597 fprintf(stderr, "Stream error while handling input font.\n");
598 exit(EXIT_FAILURE);
601 FT_Library library;
602 FT_Face face;
603 FT_Error error;
605 error = FT_Init_FreeType(&library);
606 if (error)
608 fprintf(stderr, "Can't initialize FreeType library:\n"
609 "%s\n", FT_get_error_message(error));
610 exit(EXIT_FAILURE);
613 // in a TTC, a `TTFA' table is part of the first subfont,
614 // thus we can simply pass 0 as the face index
615 error = FT_New_Memory_Face(library, in_buf, (FT_Long)in_len, 0, &face);
616 if (error)
618 fprintf(stderr, "Can't open input font:\n"
619 "%s\n", FT_get_error_message(error));
620 exit(EXIT_FAILURE);
623 FT_Byte* ttfa_buf;
624 FT_ULong ttfa_len = 0;
626 error = FT_Load_Sfnt_Table(face, TTAG_TTFA, 0, NULL, &ttfa_len);
627 if (error)
629 fprintf(stderr, "No `TTFA' table in font.\n");
630 goto Exit;
633 ttfa_buf = (FT_Byte*)malloc(ttfa_len);
634 if (!ttfa_buf)
636 fprintf(stderr, "Can't allocate enough memory.\n");
637 exit(EXIT_FAILURE);
640 error = FT_Load_Sfnt_Table(face, TTAG_TTFA, 0, ttfa_buf, &ttfa_len);
641 if (error)
643 fprintf(stderr, "Error loading `TTFA' table:\n"
644 "%s\n", FT_get_error_message(error));
645 exit(EXIT_FAILURE);
648 fprintf(stdout, "%s", ttfa_buf);
650 Exit:
651 FT_Done_Face(face);
652 FT_Done_FreeType(library);
654 free(in_buf);
655 free(ttfa_buf);
656 if (in != stdin)
657 fclose(in);
659 exit(EXIT_SUCCESS);
661 #endif
665 main(int argc,
666 char** argv)
668 int hinting_range_min = 0;
669 int hinting_range_max = 0;
670 int hinting_limit = 0;
671 int increase_x_height = 0;
672 int fallback_stem_width = 0;
674 bool have_hinting_range_min = false;
675 bool have_hinting_range_max = false;
676 bool have_hinting_limit = false;
677 bool have_increase_x_height = false;
678 bool have_fallback_stem_width = false;
680 bool gray_strong_stem_width = false;
681 bool gdi_cleartype_strong_stem_width = true;
682 bool dw_cleartype_strong_stem_width = false;
684 bool ignore_restrictions = false;
685 bool windows_compatibility = false;
686 bool adjust_subglyphs = false;
687 bool hint_composites = false;
688 bool no_info = false;
689 bool detailed_info = false;
690 bool TTFA_info = false;
691 #ifndef BUILD_GUI
692 bool show_TTFA_info = false;
693 #endif
694 bool symbol = false;
695 bool fallback_scaling = false;
697 const char* default_script = NULL;
698 bool have_default_script = false;
699 const char* fallback_script = NULL;
700 bool have_fallback_script = false;
701 const char* x_height_snapping_exceptions_string = NULL;
702 bool have_x_height_snapping_exceptions_string = false;
703 const char* family_suffix = NULL;
704 bool have_family_suffix = false;
706 bool dehint = false;
708 #ifndef BUILD_GUI
709 bool debug = false;
711 TA_Progress_Func progress_func = NULL;
712 TA_Error_Func err_func = err;
713 TA_Info_Func info_func = info;
714 TA_Info_Post_Func info_post_func = info_post;
716 const char* control_name = NULL;
717 #endif
719 // For real numbers (both parsing and displaying) we only use `.' as the
720 // decimal separator; similarly, we don't want localized formats like a
721 // thousands separator for any number.
722 setlocale(LC_NUMERIC, "C");
724 // make GNU, Qt, and X11 command line options look the same;
725 // we allow `--foo=bar', `--foo bar', `-foo=bar', `-foo bar',
726 // and short options specific to ttfautohint
728 // set up a new argument string
729 vector<string> new_arg_string;
730 new_arg_string.push_back(argv[0]);
732 while (1)
734 // use pseudo short options for long-only options
735 enum
737 PASS_THROUGH = CHAR_MAX + 1,
738 HELP_ALL_OPTION,
739 DEBUG_OPTION
742 static struct option long_options[] =
744 {"help", no_argument, NULL, 'h'},
745 #ifdef BUILD_GUI
746 {"help-all", no_argument, NULL, HELP_ALL_OPTION},
747 #endif
749 // ttfautohint options
750 {"adjust-subglyphs", no_argument, NULL, 'p'},
751 {"composites", no_argument, NULL, 'c'},
752 #ifndef BUILD_GUI
753 {"control-file", required_argument, NULL, 'm'},
754 {"debug", no_argument, NULL, DEBUG_OPTION},
755 #endif
756 {"default-script", required_argument, NULL, 'D'},
757 {"dehint", no_argument, NULL, 'd'},
758 {"detailed-info", no_argument, NULL, 'I'},
759 {"fallback-scaling", no_argument, NULL, 'S'},
760 {"fallback-script", required_argument, NULL, 'f'},
761 {"fallback-stem-width", required_argument, NULL, 'H'},
762 {"family-suffix", required_argument, NULL, 'F'},
763 {"hinting-limit", required_argument, NULL, 'G'},
764 {"hinting-range-max", required_argument, NULL, 'r'},
765 {"hinting-range-min", required_argument, NULL, 'l'},
766 {"ignore-restrictions", no_argument, NULL, 'i'},
767 {"increase-x-height", required_argument, NULL, 'x'},
768 {"no-info", no_argument, NULL, 'n'},
769 {"pre-hinting", no_argument, NULL, 'p'},
770 {"strong-stem-width", required_argument, NULL, 'w'},
771 {"symbol", no_argument, NULL, 's'},
772 {"ttfa-table", no_argument, NULL, 't'},
773 #ifndef BUILD_GUI
774 {"ttfa-info", no_argument, NULL, 'T'},
775 #endif
776 {"verbose", no_argument, NULL, 'v'},
777 {"version", no_argument, NULL, 'V'},
778 {"windows-compatibility", no_argument, NULL, 'W'},
779 {"x-height-snapping-exceptions", required_argument, NULL, 'X'},
781 // Qt options
782 {"graphicssystem", required_argument, NULL, PASS_THROUGH},
783 {"reverse", no_argument, NULL, PASS_THROUGH},
784 {"session", required_argument, NULL, PASS_THROUGH},
785 {"style", required_argument, NULL, PASS_THROUGH},
786 {"stylesheet", required_argument, NULL, PASS_THROUGH},
788 // X11 options
789 {"background", required_argument, NULL, PASS_THROUGH},
790 {"bg", required_argument, NULL, PASS_THROUGH},
791 {"btn", required_argument, NULL, PASS_THROUGH},
792 {"button", required_argument, NULL, PASS_THROUGH},
793 {"cmap", no_argument, NULL, PASS_THROUGH},
794 {"display", required_argument, NULL, PASS_THROUGH},
795 {"fg", required_argument, NULL, PASS_THROUGH},
796 {"fn", required_argument, NULL, PASS_THROUGH},
797 {"font", required_argument, NULL, PASS_THROUGH},
798 {"foreground", required_argument, NULL, PASS_THROUGH},
799 {"geometry", required_argument, NULL, PASS_THROUGH},
800 {"im", required_argument, NULL, PASS_THROUGH},
801 {"inputstyle", required_argument, NULL, PASS_THROUGH},
802 {"name", required_argument, NULL, PASS_THROUGH},
803 {"ncols", required_argument, NULL, PASS_THROUGH},
804 {"title", required_argument, NULL, PASS_THROUGH},
805 {"visual", required_argument, NULL, PASS_THROUGH},
807 {NULL, 0, NULL, 0}
810 int option_index;
811 int c = getopt_long_only(argc, argv,
812 #ifdef BUILD_GUI
813 "cdD:f:F:G:hH:iIl:npr:sStvVw:Wx:X:",
814 #else
815 "cdD:f:F:G:hH:iIl:m:npr:sStTvVw:Wx:X:",
816 #endif
817 long_options, &option_index);
818 if (c == -1)
819 break;
821 switch (c)
823 case 'c':
824 hint_composites = true;
825 break;
827 case 'd':
828 dehint = true;
829 break;
831 case 'D':
832 default_script = optarg;
833 have_default_script = true;
834 break;
836 case 'f':
837 fallback_script = optarg;
838 have_fallback_script = true;
839 break;
841 case 'F':
842 family_suffix = optarg;
843 have_family_suffix = true;
844 break;
846 case 'G':
847 hinting_limit = atoi(optarg);
848 have_hinting_limit = true;
849 break;
851 case 'h':
852 #ifdef CONSOLE_OUTPUT
853 show_help(false, false);
854 #endif
855 break;
857 case 'H':
858 fallback_stem_width = atoi(optarg);
859 have_fallback_stem_width = true;
860 break;
862 case 'i':
863 ignore_restrictions = true;
864 break;
866 case 'I':
867 detailed_info = true;
868 no_info = false;
869 break;
871 case 'l':
872 hinting_range_min = atoi(optarg);
873 have_hinting_range_min = true;
874 break;
876 #ifndef BUILD_GUI
877 case 'm':
878 control_name = optarg;
879 break;
880 #endif
882 case 'n':
883 no_info = true;
884 detailed_info = false;
885 break;
887 case 'p':
888 adjust_subglyphs = true;
889 break;
891 case 'r':
892 hinting_range_max = atoi(optarg);
893 have_hinting_range_max = true;
894 break;
896 case 's':
897 symbol = true;
898 break;
900 case 'S':
901 fallback_scaling = true;
902 break;
904 case 't':
905 TTFA_info = true;
906 break;
908 #ifndef BUILD_GUI
909 case 'T':
910 show_TTFA_info = true;
911 break;
913 case 'v':
914 progress_func = progress;
915 #endif
916 break;
918 case 'V':
919 #ifdef CONSOLE_OUTPUT
920 show_version();
921 #endif
922 break;
924 case 'w':
925 gray_strong_stem_width = strchr(optarg, 'g') ? true : false;
926 gdi_cleartype_strong_stem_width = strchr(optarg, 'G') ? true : false;
927 dw_cleartype_strong_stem_width = strchr(optarg, 'D') ? true : false;
928 break;
930 case 'W':
931 windows_compatibility = true;
932 break;
934 case 'x':
935 increase_x_height = atoi(optarg);
936 have_increase_x_height = true;
937 break;
939 case 'X':
940 x_height_snapping_exceptions_string = optarg;
941 have_x_height_snapping_exceptions_string = true;
942 break;
944 #ifndef BUILD_GUI
945 case DEBUG_OPTION:
946 debug = true;
947 break;
948 #endif
950 #ifdef BUILD_GUI
951 case HELP_ALL_OPTION:
952 #ifdef CONSOLE_OUTPUT
953 show_help(true, false);
954 #endif
955 break;
956 #endif
958 case PASS_THROUGH:
960 // append argument with proper syntax for Qt
961 string arg;
962 arg += '-';
963 arg += long_options[option_index].name;
965 new_arg_string.push_back(arg);
966 if (optarg)
967 new_arg_string.push_back(optarg);
968 break;
971 default:
972 exit(EXIT_FAILURE);
976 if (dehint)
978 // -d makes ttfautohint ignore all other hinting options
979 have_default_script = false;
980 have_fallback_script = false;
981 have_fallback_stem_width = false;
982 have_hinting_range_max = false;
983 have_hinting_range_min = false;
984 have_hinting_limit = false;
985 have_increase_x_height = false;
986 have_x_height_snapping_exceptions_string = false;
989 #ifndef BUILD_GUI
990 if (show_TTFA_info)
992 // -T makes ttfautohint ignore even more options
993 have_default_script = false;
994 have_fallback_script = false;
995 have_fallback_stem_width = false;
996 have_hinting_range_max = false;
997 have_hinting_range_min = false;
998 have_hinting_limit = false;
999 have_increase_x_height = false;
1000 have_x_height_snapping_exceptions_string = false;
1001 have_family_suffix = false;
1002 debug = false;
1004 #endif
1006 if (!have_default_script)
1007 default_script = "latn";
1008 if (!have_fallback_script)
1009 fallback_script = "none";
1010 if (!have_hinting_range_min)
1011 hinting_range_min = TA_HINTING_RANGE_MIN;
1012 if (!have_hinting_range_max)
1013 hinting_range_max = TA_HINTING_RANGE_MAX;
1014 if (!have_hinting_limit)
1015 hinting_limit = TA_HINTING_LIMIT;
1016 if (!have_increase_x_height)
1017 increase_x_height = TA_INCREASE_X_HEIGHT;
1018 if (!have_x_height_snapping_exceptions_string)
1019 x_height_snapping_exceptions_string = "";
1020 if (!have_fallback_stem_width)
1021 fallback_stem_width = 0; /* redundant, but avoids a compiler warning */
1022 if (!have_family_suffix)
1023 family_suffix = "";
1025 #ifndef BUILD_GUI
1027 if (!isatty(fileno(stderr)) && !debug)
1028 setvbuf(stderr, (char*)NULL, _IONBF, BUFSIZ);
1030 if (hinting_range_min < 2)
1032 fprintf(stderr, "The hinting range minimum must be at least 2\n");
1033 exit(EXIT_FAILURE);
1035 if (hinting_range_max < hinting_range_min)
1037 fprintf(stderr, "The hinting range maximum must not be smaller"
1038 " than the minimum (%d)\n",
1039 hinting_range_min);
1040 exit(EXIT_FAILURE);
1042 if (hinting_limit != 0 && hinting_limit < hinting_range_max)
1044 fprintf(stderr, "A non-zero hinting limit must not be smaller"
1045 " than the hinting range maximum (%d)\n",
1046 hinting_range_max);
1047 exit(EXIT_FAILURE);
1049 if (increase_x_height != 0 && increase_x_height < 6)
1051 fprintf(stderr, "A non-zero x height increase limit"
1052 " must be larger than or equal to 6\n");
1053 exit(EXIT_FAILURE);
1055 if (have_fallback_stem_width && fallback_stem_width <= 0)
1057 fprintf(stderr, "The fallback stem width"
1058 " must be a positive integer\n");
1059 exit(EXIT_FAILURE);
1062 if (have_default_script)
1064 const Tag_Names* sn;
1066 for (sn = script_names; sn->tag; sn++)
1067 if (!strcmp(default_script, sn->tag))
1068 break;
1069 if (!sn->tag)
1071 fprintf(stderr, "Unknown script tag `%s'\n", default_script);
1072 exit(EXIT_FAILURE);
1076 if (have_fallback_script)
1078 const Tag_Names* sn;
1080 for (sn = script_names; sn->tag; sn++)
1081 if (!strcmp(fallback_script, sn->tag))
1082 break;
1083 if (!sn->tag)
1085 fprintf(stderr, "Unknown script tag `%s'\n", fallback_script);
1086 exit(EXIT_FAILURE);
1090 if (symbol
1091 && have_fallback_stem_width
1092 && fallback_scaling)
1093 fprintf(stderr,
1094 "Warning: Setting a fallback stem width for a symbol font\n"
1095 " with fallback scaling only has no effect\n");
1097 if (const char* pos = check_family_suffix(family_suffix))
1099 fprintf(stderr,
1100 "Invalid character in family suffix:\n"
1101 " %s\n"
1102 " %*s\n",
1103 family_suffix,
1104 int(pos - family_suffix + 1), "^");
1105 exit(EXIT_FAILURE);
1108 int num_args = argc - optind;
1110 if (num_args > 2)
1111 show_help(false, true);
1113 FILE* in;
1114 if (num_args > 0)
1116 in = fopen(argv[optind], "rb");
1117 if (!in)
1119 fprintf(stderr,
1120 "The following error occurred while opening font `%s':\n"
1121 "\n"
1122 " %s\n",
1123 argv[optind], strerror(errno));
1124 exit(EXIT_FAILURE);
1127 else
1129 if (isatty(fileno(stdin)))
1130 show_help(false, true);
1131 in = stdin;
1134 #ifndef BUILD_GUI
1135 if (show_TTFA_info)
1136 display_TTFA(in); // this function doesn't return
1137 #endif
1139 FILE* out;
1140 if (num_args > 1)
1142 if (!strcmp(argv[optind], argv[optind + 1]))
1144 fprintf(stderr, "Input and output file names must not be identical\n");
1145 exit(EXIT_FAILURE);
1148 out = fopen(argv[optind + 1], "wb");
1149 if (!out)
1151 fprintf(stderr,
1152 "The following error occurred while opening font `%s':\n"
1153 "\n"
1154 " %s\n",
1155 argv[optind + 1], strerror(errno));
1156 exit(EXIT_FAILURE);
1159 else
1161 if (isatty(fileno(stdout)))
1162 show_help(false, true);
1163 out = stdout;
1166 FILE* control = NULL;
1167 if (control_name)
1169 control = fopen(control_name, "r");
1170 if (!control)
1172 fprintf(stderr,
1173 "The following error occurred while open control file `%s':\n"
1174 "\n"
1175 " %s\n",
1176 control_name, strerror(errno));
1177 exit(EXIT_FAILURE);
1180 else
1181 control = NULL;
1183 Progress_Data progress_data = {-1, 1, 0};
1184 Error_Data error_data = {control_name};
1185 Info_Data info_data;
1187 if (!*family_suffix)
1188 info_post_func = NULL;
1190 info_data.no_info = no_info;
1191 info_data.detailed_info = detailed_info;
1192 info_data.info_string = NULL; // must be deallocated after use
1193 info_data.info_string_wide = NULL; // must be deallocated after use
1194 info_data.info_string_len = 0;
1195 info_data.info_string_wide_len = 0;
1197 info_data.control_name = control_name;
1199 info_data.hinting_range_min = hinting_range_min;
1200 info_data.hinting_range_max = hinting_range_max;
1201 info_data.hinting_limit = hinting_limit;
1203 info_data.gray_strong_stem_width = gray_strong_stem_width;
1204 info_data.gdi_cleartype_strong_stem_width = gdi_cleartype_strong_stem_width;
1205 info_data.dw_cleartype_strong_stem_width = dw_cleartype_strong_stem_width;
1207 info_data.windows_compatibility = windows_compatibility;
1208 info_data.adjust_subglyphs = adjust_subglyphs;
1209 info_data.hint_composites = hint_composites;
1210 info_data.increase_x_height = increase_x_height;
1211 info_data.x_height_snapping_exceptions_string = x_height_snapping_exceptions_string;
1212 info_data.family_suffix = family_suffix;
1213 info_data.family_data_head = NULL;
1214 info_data.fallback_stem_width = fallback_stem_width;
1215 info_data.symbol = symbol;
1216 info_data.fallback_scaling = fallback_scaling;
1217 info_data.TTFA_info = TTFA_info;
1219 strncpy(info_data.default_script,
1220 default_script,
1221 sizeof (info_data.default_script));
1222 strncpy(info_data.fallback_script,
1223 fallback_script,
1224 sizeof (info_data.fallback_script));
1226 info_data.dehint = dehint;
1228 if (!no_info)
1230 int ret = build_version_string(&info_data);
1231 if (ret == 1)
1232 fprintf(stderr, "Warning: Can't allocate memory"
1233 " for ttfautohint options string in `name' table\n");
1234 else if (ret == 2)
1235 fprintf(stderr, "Warning: ttfautohint options string"
1236 " in `name' table too long\n");
1239 if (in == stdin)
1240 SET_BINARY(stdin);
1241 if (out == stdout)
1242 SET_BINARY(stdout);
1244 TA_Error error =
1245 TTF_autohint("in-file, out-file, control-file,"
1246 "hinting-range-min, hinting-range-max, hinting-limit,"
1247 "gray-strong-stem-width, gdi-cleartype-strong-stem-width,"
1248 "dw-cleartype-strong-stem-width,"
1249 "progress-callback, progress-callback-data,"
1250 "error-callback, error-callback-data,"
1251 "info-callback, info-post-callback, info-callback-data,"
1252 "ignore-restrictions, windows-compatibility,"
1253 "adjust-subglyphs, hint-composites,"
1254 "increase-x-height, x-height-snapping-exceptions,"
1255 "fallback-stem-width, default-script,"
1256 "fallback-script, fallback-scaling,"
1257 "symbol, dehint, debug, TTFA-info",
1258 in, out, control,
1259 hinting_range_min, hinting_range_max, hinting_limit,
1260 gray_strong_stem_width, gdi_cleartype_strong_stem_width,
1261 dw_cleartype_strong_stem_width,
1262 progress_func, &progress_data,
1263 err_func, &error_data,
1264 info_func, info_post_func, &info_data,
1265 ignore_restrictions, windows_compatibility,
1266 adjust_subglyphs, hint_composites,
1267 increase_x_height, x_height_snapping_exceptions_string,
1268 fallback_stem_width, default_script,
1269 fallback_script, fallback_scaling,
1270 symbol, dehint, debug, TTFA_info);
1272 if (!no_info)
1274 free(info_data.info_string);
1275 free(info_data.info_string_wide);
1278 if (in != stdin)
1279 fclose(in);
1280 if (out != stdout)
1281 fclose(out);
1282 if (control)
1283 fclose(control);
1285 exit(error ? EXIT_FAILURE : EXIT_SUCCESS);
1287 return 0; // never reached
1289 #else // BUILD_GUI
1291 int new_argc = (int)new_arg_string.size();
1292 char** new_argv = new char*[new_argc];
1294 // construct new argc and argv variables from collected data
1295 for (int i = 0; i < new_argc; i++)
1296 new_argv[i] = const_cast<char*>(new_arg_string[i].data());
1298 QApplication app(new_argc, new_argv);
1299 app.setApplicationName("TTFautohint");
1300 app.setApplicationVersion(VERSION);
1301 app.setOrganizationName("FreeType");
1302 app.setOrganizationDomain("freetype.org");
1304 bool alternative_layout = false;
1306 // Display the window off the screen -- to get proper window dimensions
1307 // including the frame, the window manager must have a chance to
1308 // decorate it.
1310 // We don't want to change the default window positioning algorithm of
1311 // the platform's window manager, so we create the main GUI window
1312 // twice.
1314 // The original idea, however, was to simply move the off-screen window
1315 // back to the screen with
1317 // gui.move(100, 100);
1318 // gui.setAttribute(Qt::WA_Moved, false);
1319 // gui.show();
1321 // (unsetting the `WA_Moved' attribute makes the window manager handle
1322 // the previous call to `move' as a position suggestion instead of a
1323 // request). Unfortuntely, there seems to be a bug in Qt 4.8.4 which
1324 // prevents any effect of unsetting `WA_Moved' if `show' has already
1325 // been called.
1327 Main_GUI dummy(alternative_layout,
1328 hinting_range_min, hinting_range_max, hinting_limit,
1329 gray_strong_stem_width, gdi_cleartype_strong_stem_width,
1330 dw_cleartype_strong_stem_width, increase_x_height,
1331 x_height_snapping_exceptions_string, fallback_stem_width,
1332 ignore_restrictions, windows_compatibility, adjust_subglyphs,
1333 hint_composites, no_info, detailed_info,
1334 default_script, fallback_script, fallback_scaling,
1335 family_suffix, symbol, dehint, TTFA_info);
1337 dummy.move(-50000, -50000);
1338 dummy.show();
1340 // if the vertical size of our window is too large,
1341 // select a horizontal layout
1342 QRect screen(QApplication::desktop()->availableGeometry());
1343 if (dummy.frameGeometry().height() > screen.height())
1344 alternative_layout = true;
1347 Main_GUI gui(alternative_layout,
1348 hinting_range_min, hinting_range_max, hinting_limit,
1349 gray_strong_stem_width, gdi_cleartype_strong_stem_width,
1350 dw_cleartype_strong_stem_width, increase_x_height,
1351 x_height_snapping_exceptions_string, fallback_stem_width,
1352 ignore_restrictions, windows_compatibility, adjust_subglyphs,
1353 hint_composites, no_info, detailed_info,
1354 default_script, fallback_script, fallback_scaling,
1355 family_suffix, symbol, dehint, TTFA_info);
1356 gui.show();
1358 return app.exec();
1360 #endif // BUILD_GUI
1363 // end of main.cpp