Synchronize frontends with library.
[ttfautohint.git] / frontend / main.cpp
blobc76f8c6e449d09ac06d41588ef4a32b5cc3e5770
1 // main.cpp
3 // Copyright (C) 2011-2012 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>
33 #include <vector>
34 #include <string>
36 #if BUILD_GUI
37 # include <QApplication>
38 # include "maingui.h"
39 #else
40 # include "info.h"
41 #endif
43 #include <ttfautohint.h>
45 using namespace std;
48 #ifndef BUILD_GUI
49 extern "C" {
51 typedef struct Progress_Data_
53 long last_sfnt;
54 bool begin;
55 int last_percent;
56 } Progress_Data;
59 int
60 progress(long curr_idx,
61 long num_glyphs,
62 long curr_sfnt,
63 long num_sfnts,
64 void* user)
66 Progress_Data* data = (Progress_Data*)user;
68 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
70 fprintf(stderr, "subfont %ld of %ld\n", curr_sfnt + 1, num_sfnts);
71 data->last_sfnt = curr_sfnt;
72 data->begin = true;
75 if (data->begin)
77 fprintf(stderr, " %ld glyphs\n"
78 " ", num_glyphs);
79 data->begin = false;
82 // print progress approx. every 10%
83 int curr_percent = curr_idx * 100 / num_glyphs;
84 int curr_diff = curr_percent - data->last_percent;
86 if (curr_diff >= 10)
88 fprintf(stderr, " %d%%", curr_percent);
89 data->last_percent = curr_percent - curr_percent % 10;
92 if (curr_idx + 1 == num_glyphs)
93 fprintf(stderr, "\n");
95 return 0;
98 } // extern "C"
99 #endif // !BUILD_GUI
102 #ifdef CONSOLE_OUTPUT
103 static void
104 show_help(bool
105 #ifdef BUILD_GUI
107 #endif
109 bool is_error)
111 FILE* handle = is_error ? stderr : stdout;
113 fprintf(handle,
114 #ifdef BUILD_GUI
115 "Usage: ttfautohintGUI [OPTION]...\n"
116 "A GUI application to replace hints in a TrueType font.\n"
117 #else
118 "Usage: ttfautohint [OPTION]... IN-FILE OUT-FILE\n"
119 "Replace hints in TrueType font IN-FILE and write output to OUT-FILE.\n"
120 #endif
121 "The new hints are based on FreeType's autohinter.\n"
122 "\n"
123 "This program is a simple front-end to the `ttfautohint' library.\n"
124 "\n");
126 fprintf(handle,
127 "Long options can be given with one or two dashes,\n"
128 "and with and without equal sign between option and argument.\n"
129 "This means that the following forms are acceptable:\n"
130 "`-foo=bar', `--foo=bar', `-foo bar', `--foo bar'.\n"
131 "\n"
132 "Mandatory arguments to long options are mandatory for short options too.\n"
133 #ifdef BUILD_GUI
134 "Options not related to Qt or X11 set default values.\n"
135 #endif
136 "\n"
139 fprintf(handle,
140 "Options:\n"
141 #ifndef BUILD_GUI
142 " --debug print debugging information\n"
143 #endif
144 " -c, --components hint glyph components separately\n"
145 " -f, --latin-fallback set fallback script to latin\n"
146 " -G, --hinting-limit=N switch off hinting above this PPEM value\n"
147 " (default: %d); value 0 means no limit\n"
148 " -h, --help display this help and exit\n"
149 #ifdef BUILD_GUI
150 " --help-all show Qt and X11 specific options also\n"
151 #endif
152 " -i, --ignore-restrictions override font license restrictions\n"
153 " -l, --hinting-range-min=N the minimum PPEM value for hint sets\n"
154 " (default: %d)\n"
155 " -n --no-info don't add ttfautohint info\n"
156 " to the version string(s) in the `name' table\n"
157 " -p, --pre-hinting apply original hints in advance\n",
158 TA_HINTING_LIMIT, TA_HINTING_RANGE_MIN);
159 fprintf(handle,
160 " -r, --hinting-range-max=N the maximum PPEM value for hint sets\n"
161 " (default: %d)\n"
162 " -s, --symbol input is symbol font\n"
163 " -v, --verbose show progress information\n"
164 " -V, --version print version information and exit\n"
165 " -w, --strong-stem-width=S use strong stem width routine for modes S,\n"
166 " where S is a string of up to three letters\n"
167 " with possible values `g' for grayscale,\n"
168 " `G' for GDI ClearType, and `D' for\n"
169 " DirectWrite ClearType (default: G)\n"
170 " -x, --increase-x-height=N increase x height for sizes in the range\n"
171 " 6<=PPEM<=N; value 0 switches off this feature\n"
172 " (default: %d)\n"
173 " -X, --x-height-snapping-exceptions=STRING\n"
174 " specify a comma-separated list of\n"
175 " x-height snapping exceptions\n"
176 "\n",
177 TA_HINTING_RANGE_MAX, TA_INCREASE_X_HEIGHT);
179 #ifdef BUILD_GUI
180 if (all)
182 fprintf(handle,
183 "Qt Options:\n"
184 " --graphicssystem=SYSTEM\n"
185 " select a different graphics system backend\n"
186 " instead of the default one\n"
187 " (possible values: `raster', `opengl')\n"
188 " --reverse set layout direction to right-to-left\n");
189 fprintf(handle,
190 " --session=ID restore the application for the given ID\n"
191 " --style=STYLE set application GUI style\n"
192 " (possible values: motif, windows, platinum)\n"
193 " --stylesheet=SHEET apply the given Qt stylesheet\n"
194 " to the application widgets\n"
195 "\n");
197 fprintf(handle,
198 "X11 options:\n"
199 " --background=COLOR set the default background color\n"
200 " and an application palette\n"
201 " (light and dark shades are calculated)\n"
202 " --bg=COLOR same as --background\n"
203 " --btn=COLOR set the default button color\n"
204 " --button=COLOR same as --btn\n"
205 " --cmap use a private color map on an 8-bit display\n"
206 " --display=NAME use the given X-server display\n");
207 fprintf(handle,
208 " --fg=COLOR set the default foreground color\n"
209 " --fn=FONTNAME set the application font\n"
210 " --font=FONTNAME same as --fn\n"
211 " --foreground=COLOR same as --fg\n"
212 " --geometry=GEOMETRY set the client geometry of first window\n"
213 " --im=SERVER set the X Input Method (XIM) server\n"
214 " --inputstyle=STYLE set X Input Method input style\n"
215 " (possible values: onthespot, overthespot,\n"
216 " offthespot, root)\n");
217 fprintf(handle,
218 " --name=NAME set the application name\n"
219 " --ncols=COUNT limit the number of colors allocated\n"
220 " in the color cube on an 8-bit display,\n"
221 " if the application is using the\n"
222 " QApplication::ManyColor color specification\n"
223 " --title=TITLE set the application title (caption)\n"
224 " --visual=VISUAL force the application\n"
225 " to use the given visual on an 8-bit display\n"
226 " (only possible value: TrueColor)\n"
227 "\n");
229 #endif // BUILD_GUI
231 fprintf(handle,
232 "The program accepts both TTF and TTC files as input.\n"
233 "Use option -i only if you have a legal permission to modify the font.\n"
234 "If option -f is not set, glyphs not in the latin range stay unhinted.\n"
235 "The used PPEM value for option -p is FUnits per em, normally 2048.\n"
236 "With option -s, use default values for standard stem width and height,\n"
237 "otherwise they are derived from latin character `o'.\n"
238 "\n");
239 fprintf(handle,
240 "A hint set contains the optimal hinting for a certain PPEM value;\n"
241 "the larger the hint set range, the more hint sets get computed,\n"
242 "usually increasing the output font size. Note, however,\n"
243 "that the `gasp' table of the output file enables grayscale hinting\n"
244 "for all sizes (limited by option -G which is handled in the bytecode).\n"
245 "\n");
246 fprintf(handle,
247 #ifdef BUILD_GUI
248 "A command-line version of this program is called `ttfautohint'.\n"
249 #else
250 "A GUI version of this program is called `ttfautohintGUI'.\n"
251 #endif
252 "\n"
253 "Report bugs to: freetype-devel@nongnu.org\n"
254 "ttfautohint home page: <http://www.freetype.org/ttfautohint>\n");
256 if (is_error)
257 exit(EXIT_FAILURE);
258 else
259 exit(EXIT_SUCCESS);
263 static void
264 show_version()
266 fprintf(stdout,
267 #ifdef BUILD_GUI
268 "ttfautohintGUI " VERSION "\n"
269 #else
270 "ttfautohint " VERSION "\n"
271 #endif
272 "Copyright (C) 2011-2012 Werner Lemberg <wl@gnu.org>.\n"
273 "License: FreeType License (FTL) or GNU GPLv2.\n"
274 "This is free software: you are free to change and redistribute it.\n"
275 "There is NO WARRANTY, to the extent permitted by law.\n");
277 exit(EXIT_SUCCESS);
279 #endif // CONSOLE_OUTPUT
283 main(int argc,
284 char** argv)
286 int hinting_range_min = 0;
287 int hinting_range_max = 0;
288 int hinting_limit = 0;
289 int increase_x_height = 0;
290 bool have_hinting_range_min = false;
291 bool have_hinting_range_max = false;
292 bool have_hinting_limit = false;
293 bool have_increase_x_height = false;
295 bool gray_strong_stem_width = false;
296 bool gdi_cleartype_strong_stem_width = true;
297 bool dw_cleartype_strong_stem_width = false;
299 bool ignore_restrictions = false;
300 bool pre_hinting = false;
301 bool hint_with_components = true;
302 bool no_info = false;
303 int latin_fallback = 0; // leave it as int; this probably gets extended
304 bool symbol = false;
306 #ifndef BUILD_GUI
307 bool debug = false;
309 TA_Progress_Func progress_func = NULL;
310 TA_Info_Func info_func = info;
311 #endif
313 // make GNU, Qt, and X11 command line options look the same;
314 // we allow `--foo=bar', `--foo bar', `-foo=bar', `-foo bar',
315 // and short options specific to ttfautohint
317 // set up a new argument string
318 vector<string> new_arg_string;
319 new_arg_string.push_back(argv[0]);
321 while (1)
323 // use pseudo short options for long-only options
324 enum
326 PASS_THROUGH = CHAR_MAX + 1,
327 HELP_ALL_OPTION,
328 DEBUG_OPTION
331 static struct option long_options[] =
333 {"help", no_argument, NULL, 'h'},
334 #ifdef BUILD_GUI
335 {"help-all", no_argument, NULL, HELP_ALL_OPTION},
336 #endif
338 // ttfautohint options
339 {"components", no_argument, NULL, 'c'},
340 #ifndef BUILD_GUI
341 {"debug", no_argument, NULL, DEBUG_OPTION},
342 #endif
343 {"hinting-limit", required_argument, NULL, 'G'},
344 {"hinting-range-max", required_argument, NULL, 'r'},
345 {"hinting-range-min", required_argument, NULL, 'l'},
346 {"ignore-restrictions", no_argument, NULL, 'i'},
347 {"increase-x-height", required_argument, NULL, 'x'},
348 {"latin-fallback", no_argument, NULL, 'f'},
349 {"no-info", no_argument, NULL, 'n'},
350 {"pre-hinting", no_argument, NULL, 'p'},
351 {"strong-stem-width", required_argument, NULL, 'w'},
352 {"symbol", no_argument, NULL, 's'},
353 {"verbose", no_argument, NULL, 'v'},
354 {"version", no_argument, NULL, 'V'},
355 {"x-height-snapping-exceptions", required_argument, NULL, 'X'},
357 // Qt options
358 {"graphicssystem", required_argument, NULL, PASS_THROUGH},
359 {"reverse", no_argument, NULL, PASS_THROUGH},
360 {"session", required_argument, NULL, PASS_THROUGH},
361 {"style", required_argument, NULL, PASS_THROUGH},
362 {"stylesheet", required_argument, NULL, PASS_THROUGH},
364 // X11 options
365 {"background", required_argument, NULL, PASS_THROUGH},
366 {"bg", required_argument, NULL, PASS_THROUGH},
367 {"btn", required_argument, NULL, PASS_THROUGH},
368 {"button", required_argument, NULL, PASS_THROUGH},
369 {"cmap", no_argument, NULL, PASS_THROUGH},
370 {"display", required_argument, NULL, PASS_THROUGH},
371 {"fg", required_argument, NULL, PASS_THROUGH},
372 {"fn", required_argument, NULL, PASS_THROUGH},
373 {"font", required_argument, NULL, PASS_THROUGH},
374 {"foreground", required_argument, NULL, PASS_THROUGH},
375 {"geometry", required_argument, NULL, PASS_THROUGH},
376 {"im", required_argument, NULL, PASS_THROUGH},
377 {"inputstyle", required_argument, NULL, PASS_THROUGH},
378 {"name", required_argument, NULL, PASS_THROUGH},
379 {"ncols", required_argument, NULL, PASS_THROUGH},
380 {"title", required_argument, NULL, PASS_THROUGH},
381 {"visual", required_argument, NULL, PASS_THROUGH},
383 {NULL, 0, NULL, 0}
386 int option_index;
387 int c = getopt_long_only(argc, argv, "cfG:hil:npr:stVvx:X:w:",
388 long_options, &option_index);
389 if (c == -1)
390 break;
392 switch (c)
394 case 'c':
395 hint_with_components = false;
396 break;
398 case 'f':
399 latin_fallback = 1;
400 break;
402 case 'G':
403 hinting_limit = atoi(optarg);
404 have_hinting_limit = true;
405 break;
407 case 'h':
408 #ifdef CONSOLE_OUTPUT
409 show_help(false, false);
410 #endif
411 break;
413 case 'i':
414 ignore_restrictions = true;
415 break;
417 case 'l':
418 hinting_range_min = atoi(optarg);
419 have_hinting_range_min = true;
420 break;
422 case 'n':
423 no_info = true;
424 break;
426 case 'p':
427 pre_hinting = true;
428 break;
430 case 'r':
431 hinting_range_max = atoi(optarg);
432 have_hinting_range_max = true;
433 break;
435 case 's':
436 symbol = true;
437 break;
439 case 'v':
440 #ifndef BUILD_GUI
441 progress_func = progress;
442 #endif
443 break;
445 case 'V':
446 #ifdef CONSOLE_OUTPUT
447 show_version();
448 #endif
449 break;
451 case 'w':
452 gray_strong_stem_width = strchr(optarg, 'g') ? true : false;
453 gdi_cleartype_strong_stem_width = strchr(optarg, 'G') ? true : false;
454 dw_cleartype_strong_stem_width = strchr(optarg, 'D') ? true : false;
455 break;
457 case 'x':
458 increase_x_height = atoi(optarg);
459 have_increase_x_height = true;
460 break;
462 case 'X':
463 #ifdef CONSOLE_OUTPUT
464 fprintf(stderr, "Option `-x' not implemented yet\n");
465 #endif
466 break;
468 #ifndef BUILD_GUI
469 case DEBUG_OPTION:
470 debug = true;
471 break;
472 #endif
474 #ifdef BUILD_GUI
475 case HELP_ALL_OPTION:
476 #ifdef CONSOLE_OUTPUT
477 show_help(true, false);
478 #endif
479 break;
480 #endif
482 case PASS_THROUGH:
484 // append argument with proper syntax for Qt
485 string arg;
486 arg += '-';
487 arg += long_options[option_index].name;
489 new_arg_string.push_back(arg);
490 if (optarg)
491 new_arg_string.push_back(optarg);
492 break;
495 default:
496 exit(EXIT_FAILURE);
500 if (!have_hinting_range_min)
501 hinting_range_min = TA_HINTING_RANGE_MIN;
502 if (!have_hinting_range_max)
503 hinting_range_max = TA_HINTING_RANGE_MAX;
504 if (!have_hinting_limit)
505 hinting_limit = TA_HINTING_LIMIT;
506 if (!have_increase_x_height)
507 increase_x_height = TA_INCREASE_X_HEIGHT;
509 #ifndef BUILD_GUI
511 if (hinting_range_min < 2)
513 fprintf(stderr, "The hinting range minimum must be at least 2\n");
514 exit(EXIT_FAILURE);
516 if (hinting_range_max < hinting_range_min)
518 fprintf(stderr, "The hinting range maximum must not be smaller"
519 " than the minimum (%d)\n",
520 hinting_range_min);
521 exit(EXIT_FAILURE);
523 if (hinting_limit != 0 && hinting_limit < hinting_range_max)
525 fprintf(stderr, "A non-zero hinting limit must not be smaller"
526 " than the hinting range maximum (%d)\n",
527 hinting_range_max);
528 exit(EXIT_FAILURE);
530 if (increase_x_height != 0
531 && (increase_x_height < 6 || increase_x_height > 20))
533 fprintf(stderr, "A non-zero x height increase limit"
534 " must be in the range 6-20\n");
535 exit(EXIT_FAILURE);
538 // on the console we need in and out file arguments
539 if (argc - optind != 2)
540 show_help(false, true);
542 FILE* in = fopen(argv[optind], "rb");
543 if (!in)
545 fprintf(stderr, "The following error occurred while opening font `%s':\n"
546 "\n"
547 " %s\n",
548 argv[optind], strerror(errno));
549 exit(EXIT_FAILURE);
552 FILE* out = fopen(argv[optind + 1], "wb");
553 if (!out)
555 fprintf(stderr, "The following error occurred while opening font `%s':\n"
556 "\n"
557 " %s\n",
558 argv[optind + 1], strerror(errno));
559 exit(EXIT_FAILURE);
562 unsigned char version_data[128];
563 unsigned char version_data_wide[256];
565 const unsigned char* error_string;
566 Progress_Data progress_data = {-1, 1, 0};
567 Info_Data info_data;
569 if (no_info)
570 info_func = NULL;
571 else
573 info_data.data = version_data;
574 info_data.data_wide = version_data_wide;
576 info_data.hinting_range_min = hinting_range_min;
577 info_data.hinting_range_max = hinting_range_max;
578 info_data.hinting_limit = hinting_limit;
580 info_data.gray_strong_stem_width = gray_strong_stem_width;
581 info_data.gdi_cleartype_strong_stem_width = gdi_cleartype_strong_stem_width;
582 info_data.dw_cleartype_strong_stem_width = dw_cleartype_strong_stem_width;
584 info_data.pre_hinting = pre_hinting;
585 info_data.hint_with_components = hint_with_components;
586 info_data.increase_x_height = increase_x_height;
587 info_data.latin_fallback = latin_fallback;
588 info_data.symbol = symbol;
590 build_version_string(&info_data);
593 TA_Error error =
594 TTF_autohint("in-file, out-file,"
595 "hinting-range-min, hinting-range-max, hinting-limit,"
596 "gray-strong-stem-width, gdi-cleartype-strong-stem-width,"
597 "dw-cleartype-strong-stem-width,"
598 "error-string,"
599 "progress-callback, progress-callback-data,"
600 "info-callback, info-callback-data,"
601 "ignore-restrictions, pre-hinting, hint-with-components,"
602 "increase-x-height, fallback-script, symbol,"
603 "debug",
604 in, out,
605 hinting_range_min, hinting_range_max, hinting_limit,
606 gray_strong_stem_width, gdi_cleartype_strong_stem_width,
607 dw_cleartype_strong_stem_width,
608 &error_string,
609 progress_func, &progress_data,
610 info_func, &info_data,
611 ignore_restrictions, pre_hinting, hint_with_components,
612 increase_x_height, latin_fallback, symbol,
613 debug);
615 if (error)
617 if (error == TA_Err_Invalid_FreeType_Version)
618 fprintf(stderr,
619 "FreeType version 2.4.5 or higher is needed.\n"
620 "Perhaps using a wrong FreeType DLL?\n");
621 else if (error == TA_Err_Invalid_Font_Type)
622 fprintf(stderr,
623 "This font is not a valid font"
624 " in SFNT format with TrueType outlines.\n"
625 "In particular, CFF outlines are not supported.\n");
626 else if (error == TA_Err_Already_Processed)
627 fprintf(stderr,
628 "This font has already been processed with ttfautohint.\n");
629 else if (error == TA_Err_Missing_Legal_Permission)
630 fprintf(stderr,
631 "Bit 1 in the `fsType' field of the `OS/2' table is set:\n"
632 "This font must not be modified"
633 " without permission of the legal owner.\n"
634 "Use command line option `-i' to continue"
635 " if you have such a permission.\n");
636 else if (error == TA_Err_Missing_Unicode_CMap)
637 fprintf(stderr,
638 "No Unicode character map.\n");
639 else if (error == TA_Err_Missing_Symbol_CMap)
640 fprintf(stderr,
641 "No symbol character map.\n");
642 else if (error == TA_Err_Missing_Glyph)
643 fprintf(stderr,
644 "No glyph for the key character"
645 " to derive standard width and height.\n"
646 "For the latin script, this key character is `o' (U+006F).\n");
647 else
648 fprintf(stderr,
649 "Error code `0x%02x' while autohinting font:\n"
650 " %s\n", error, error_string);
651 exit(EXIT_FAILURE);
654 fclose(in);
655 fclose(out);
657 exit(EXIT_SUCCESS);
659 return 0; // never reached
661 #else // BUILD_GUI
663 int new_argc = new_arg_string.size();
664 char** new_argv = new char*[new_argc];
666 // construct new argc and argv variables from collected data
667 for (int i = 0; i < new_argc; i++)
668 new_argv[i] = const_cast<char*>(new_arg_string[i].data());
670 QApplication app(new_argc, new_argv);
671 app.setApplicationName("TTFautohint");
672 app.setApplicationVersion(VERSION);
673 app.setOrganizationName("FreeType");
674 app.setOrganizationDomain("freetype.org");
676 Main_GUI gui(hinting_range_min, hinting_range_max, hinting_limit,
677 gray_strong_stem_width, gdi_cleartype_strong_stem_width,
678 dw_cleartype_strong_stem_width,
679 increase_x_height, ignore_restrictions, pre_hinting,
680 hint_with_components, no_info, latin_fallback, symbol);
681 gui.show();
683 return app.exec();
685 #endif // BUILD_GUI
688 // end of main.cpp