Typo.
[ttfautohint.git] / frontend / maingui.cpp
blob93e5722fa5eeb235191dfedb0f3ce39009143de8
1 // maingui.cpp
3 // Copyright (C) 2012-2014 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 #include <config.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <locale.h>
21 #include <QtGui>
23 #include "info.h"
24 #include "maingui.h"
26 #include <ttfautohint.h>
29 // XXX Qt 4.8 bug: locale->quoteString("foo")
30 // inserts wrongly encoded quote characters
31 // into rich text QString
32 #if HAVE_QT_QUOTESTRING
33 # define QUOTE_STRING(x) locale->quoteString(x)
34 # define QUOTE_STRING_LITERAL(x) locale->quoteString(x)
35 #else
36 # define QUOTE_STRING(x) "\"" + x + "\""
37 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
38 #endif
41 // Shorthand for `tr' using a local `TRDOMAIN'.
42 #define Tr(text) QCoreApplication::translate(TRDOMAIN, text)
45 // the available script tags and its descriptions are directly extracted
46 // from `ttfautohint-scripts.h'
47 typedef struct Script_Names_
49 const char* tag;
50 const char* description;
51 } Script_Names;
53 #undef SCRIPT
54 #define SCRIPT(s, S, d, h, sc1, sc2, sc3) \
55 {#s, d},
57 const Script_Names script_names[] =
59 #include <ttfautohint-scripts.h>
60 {NULL, NULL}
64 // used hotkeys:
65 // a: Add ttf&autohint Info
66 // b: Add TTFA info ta&ble
67 // c: x Height In&crease Limit / No x Height In&crease
68 // d: &Dehint
69 // e: Control Instructions Fil&e
70 // f: &File (menu)
71 // g: Stron&g Stem Width and Positioning
72 // h: &Help (menu)
73 // i: &Input File
74 // j: Ad&just Subglyphs
75 // k: Fallbac&k Stem Width / Default Fallbac&k Stem Width
76 // l: Hinting &Limit / No Hinting &Limit
77 // m: Hint Co&mposites
78 // n: Hint Set Range Mi&nimum
79 // o: &Output File
80 // p: Windows Com&patibility
81 // q: --
82 // r: &Run
83 // s: Fallback &Script
84 // t: x Height Snapping Excep&tions
85 // u: Defa&ult Script
86 // v: --
87 // w: &Watch Input Files
88 // x: Hint Set Range Ma&ximum
89 // y: S&ymbol Font
90 // z: --
92 Main_GUI::Main_GUI(bool horizontal_layout,
93 int range_min,
94 int range_max,
95 int limit,
96 bool gray,
97 bool gdi,
98 bool dw,
99 int increase,
100 const char* exceptions,
101 int stem_width,
102 bool ignore,
103 bool wincomp,
104 bool adjust,
105 bool composites,
106 bool no,
107 bool detailed,
108 const char* dflt,
109 const char* fallback,
110 const char* suffix,
111 bool symb,
112 bool dh,
113 bool TTFA)
114 : hinting_range_min(range_min),
115 hinting_range_max(range_max),
116 hinting_limit(limit),
117 gray_strong_stem_width(gray),
118 gdi_cleartype_strong_stem_width(gdi),
119 dw_cleartype_strong_stem_width(dw),
120 increase_x_height(increase),
121 x_height_snapping_exceptions_string(exceptions),
122 fallback_stem_width(stem_width),
123 ignore_restrictions(ignore),
124 windows_compatibility(wincomp),
125 adjust_subglyphs(adjust),
126 hint_composites(composites),
127 no_info(no),
128 detailed_info(detailed),
129 family_suffix(suffix),
130 symbol(symb),
131 dehint(dh),
132 TTFA_info(TTFA)
134 int i;
136 // map default script tag to an index,
137 // replacing an invalid one with the default value
138 int latn_script_idx = 0;
139 for (i = 0; script_names[i].tag; i++)
141 if (!strcmp("latn", script_names[i].tag))
142 latn_script_idx = i;
143 if (!strcmp(dflt, script_names[i].tag))
144 break;
146 default_script_idx = script_names[i].tag ? i : latn_script_idx;
148 // map fallback script tag to an index,
149 // replacing an invalid one with the default value
150 int none_script_idx = 0;
151 for (i = 0; script_names[i].tag; i++)
153 if (!strcmp("none", script_names[i].tag))
154 none_script_idx = i;
155 if (!strcmp(fallback, script_names[i].tag))
156 break;
158 fallback_script_idx = script_names[i].tag ? i : none_script_idx;
160 x_height_snapping_exceptions = NULL;
162 // if file watching is active, we regularly poll the file status
163 timer = new QTimer(this);
164 timer->setInterval(1000);
165 fileinfo_input_file.setCaching(false);
166 fileinfo_control_file.setCaching(false);
168 // XXX register translations somewhere and loop over them
169 if (QLocale::system().name() == "en_US")
170 locale = new QLocale;
171 else
172 locale = new QLocale(QLocale::C);
174 // For real numbers (both parsing and displaying) we only use `.' as the
175 // decimal separator; similarly, we don't want localized formats like a
176 // thousands separator for any number.
177 setlocale(LC_NUMERIC, "C");
179 create_layout(horizontal_layout);
180 create_connections();
181 create_actions();
182 create_menus();
183 create_status_bar();
185 set_defaults();
186 read_settings();
188 setUnifiedTitleAndToolBarOnMac(true);
192 Main_GUI::~Main_GUI()
194 number_set_free(x_height_snapping_exceptions);
198 // overloading
200 void
201 Main_GUI::closeEvent(QCloseEvent* event)
203 write_settings();
204 event->accept();
208 void
209 Main_GUI::about()
211 QMessageBox::about(this,
212 tr("About TTFautohint"),
213 tr("<p>This is <b>TTFautohint</b> version %1<br>"
214 " Copyright %2 2011-2014<br>"
215 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
217 "<p><b>TTFautohint</b> adds new auto-generated hints"
218 " to a TrueType font or TrueType collection.</p>"
220 "<p>License:"
221 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT'>FreeType"
222 " License (FTL)</a> or"
223 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT'>GNU"
224 " GPLv2</a></p>")
225 .arg(VERSION)
226 .arg(QChar(0xA9)));
230 void
231 Main_GUI::browse_input()
233 // XXX remember last directory
234 QString file = QFileDialog::getOpenFileName(
235 this,
236 tr("Open Input File"),
237 QDir::homePath(),
238 "");
240 if (!file.isEmpty())
241 input_line->setText(QDir::toNativeSeparators(file));
245 void
246 Main_GUI::browse_output()
248 // XXX remember last directory
249 QString file = QFileDialog::getSaveFileName(
250 this,
251 tr("Open Output File"),
252 QDir::homePath(),
253 "");
255 if (!file.isEmpty())
256 output_line->setText(QDir::toNativeSeparators(file));
260 void
261 Main_GUI::browse_control()
263 // XXX remember last directory
264 QString file = QFileDialog::getOpenFileName(
265 this,
266 tr("Open Control INstructions File"),
267 QDir::homePath(),
268 "");
270 if (!file.isEmpty())
271 control_line->setText(QDir::toNativeSeparators(file));
275 void
276 Main_GUI::check_min()
278 int min = min_box->value();
279 int max = max_box->value();
280 int limit = limit_box->value();
281 if (min > max)
282 max_box->setValue(min);
283 if (min > limit)
284 limit_box->setValue(min);
288 void
289 Main_GUI::check_max()
291 int min = min_box->value();
292 int max = max_box->value();
293 int limit = limit_box->value();
294 if (max < min)
295 min_box->setValue(max);
296 if (max > limit)
297 limit_box->setValue(max);
301 void
302 Main_GUI::check_limit()
304 int min = min_box->value();
305 int max = max_box->value();
306 int limit = limit_box->value();
307 if (limit < max)
308 max_box->setValue(limit);
309 if (limit < min)
310 min_box->setValue(limit);
314 void
315 Main_GUI::check_dehint()
317 if (dehint_box->isChecked())
319 min_label->setEnabled(false);
320 min_box->setEnabled(false);
322 max_label->setEnabled(false);
323 max_box->setEnabled(false);
325 default_label->setEnabled(false);
326 default_box->setEnabled(false);
327 fallback_label->setEnabled(false);
328 fallback_box->setEnabled(false);
330 no_limit_box->setEnabled(false);
331 limit_label->setEnabled(false);
332 limit_box->setEnabled(false);
334 no_increase_box->setEnabled(false);
335 increase_label->setEnabled(false);
336 increase_box->setEnabled(false);
338 snapping_label->setEnabled(false);
339 snapping_line->setEnabled(false);
341 default_stem_width_box->setEnabled(false);
342 stem_width_label->setEnabled(false);
343 stem_width_box->setEnabled(false);
345 wincomp_box->setEnabled(false);
346 adjust_box->setEnabled(false);
347 hint_box->setEnabled(false);
348 symbol_box->setEnabled(false);
350 stem_label->setEnabled(false);
351 gray_box->setEnabled(false);
352 gdi_box->setEnabled(false);
353 dw_box->setEnabled(false);
355 else
357 min_label->setEnabled(true);
358 min_box->setEnabled(true);
360 max_label->setEnabled(true);
361 max_box->setEnabled(true);
363 default_label->setEnabled(true);
364 default_box->setEnabled(true);
365 fallback_label->setEnabled(true);
366 fallback_box->setEnabled(true);
368 no_limit_box->setEnabled(true);
369 check_no_limit();
371 no_increase_box->setEnabled(true);
372 check_no_increase();
374 snapping_label->setEnabled(true);
375 snapping_line->setEnabled(true);
377 default_stem_width_box->setEnabled(true);
378 check_default_stem_width();
380 wincomp_box->setEnabled(true);
381 adjust_box->setEnabled(true);
382 hint_box->setEnabled(true);
383 symbol_box->setEnabled(true);
385 stem_label->setEnabled(true);
386 gray_box->setEnabled(true);
387 gdi_box->setEnabled(true);
388 dw_box->setEnabled(true);
393 void
394 Main_GUI::check_no_limit()
396 if (no_limit_box->isChecked())
398 limit_label->setEnabled(false);
399 limit_label->setText(limit_label_text);
400 limit_box->setEnabled(false);
401 no_limit_box->setText(no_limit_box_text_with_key);
403 else
405 limit_label->setEnabled(true);
406 limit_label->setText(limit_label_text_with_key);
407 limit_box->setEnabled(true);
408 no_limit_box->setText(no_limit_box_text);
413 void
414 Main_GUI::check_no_increase()
416 if (no_increase_box->isChecked())
418 increase_label->setEnabled(false);
419 increase_label->setText(increase_label_text);
420 increase_box->setEnabled(false);
421 no_increase_box->setText(no_increase_box_text_with_key);
423 else
425 increase_label->setEnabled(true);
426 increase_label->setText(increase_label_text_with_key);
427 increase_box->setEnabled(true);
428 no_increase_box->setText(no_increase_box_text);
433 void
434 Main_GUI::check_default_stem_width()
436 if (default_stem_width_box->isChecked())
438 stem_width_label->setEnabled(false);
439 stem_width_label->setText(stem_width_label_text);
440 stem_width_box->setEnabled(false);
441 default_stem_width_box->setText(default_stem_width_box_text_with_key);
443 else
445 stem_width_label->setEnabled(true);
446 stem_width_label->setText(stem_width_label_text_with_key);
447 stem_width_box->setEnabled(true);
448 default_stem_width_box->setText(default_stem_width_box_text);
453 void
454 Main_GUI::check_run()
456 if (input_line->text().isEmpty() || output_line->text().isEmpty())
457 run_button->setEnabled(false);
458 else
459 run_button->setEnabled(true);
463 void
464 Main_GUI::absolute_input()
466 QString input_name = QDir::fromNativeSeparators(input_line->text());
467 if (!input_name.isEmpty()
468 && QDir::isRelativePath(input_name))
470 QDir cur_path(QDir::currentPath() + "/" + input_name);
471 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
476 void
477 Main_GUI::absolute_output()
479 QString output_name = QDir::fromNativeSeparators(output_line->text());
480 if (!output_name.isEmpty()
481 && QDir::isRelativePath(output_name))
483 QDir cur_path(QDir::currentPath() + "/" + output_name);
484 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
489 void
490 Main_GUI::absolute_control()
492 QString control_name = QDir::fromNativeSeparators(control_line->text());
493 if (!control_name.isEmpty()
494 && QDir::isRelativePath(control_name))
496 QDir cur_path(QDir::currentPath() + "/" + control_name);
497 control_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
502 void
503 Main_GUI::check_number_set()
505 QString text = snapping_line->text();
506 QString qs;
508 // construct ASCII string from arbitrary Unicode data;
509 // the idea is to accept, say, CJK fullwidth digits also
510 for (int i = 0; i < text.size(); i++)
512 QChar c = text.at(i);
514 int digit = c.digitValue();
515 if (digit >= 0)
516 qs += QString::number(digit);
517 else if (c.isSpace())
518 qs += ' ';
519 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
520 // to the `-' key in some Japanese input methods
521 else if (c.category() == QChar::Punctuation_Dash
522 || c == QChar(0x30FC))
523 qs += '-';
524 // various Unicode COMMA characters,
525 // including representation forms
526 else if (c == QChar(',')
527 || c == QChar(0x055D)
528 || c == QChar(0x060C)
529 || c == QChar(0x07F8)
530 || c == QChar(0x1363)
531 || c == QChar(0x1802)
532 || c == QChar(0x1808)
533 || c == QChar(0x3001)
534 || c == QChar(0xA4FE)
535 || c == QChar(0xA60D)
536 || c == QChar(0xA6F5)
537 || c == QChar(0xFE10)
538 || c == QChar(0xFE11)
539 || c == QChar(0xFE50)
540 || c == QChar(0xFE51)
541 || c == QChar(0xFF0C)
542 || c == QChar(0xFF64))
543 qs += ',';
544 else
545 qs += c; // we do error handling below
548 if (x_height_snapping_exceptions)
549 number_set_free(x_height_snapping_exceptions);
551 QByteArray str = qs.toLocal8Bit();
552 const char* s = number_set_parse(str.constData(),
553 &x_height_snapping_exceptions,
554 6, 0x7FFF);
555 if (s && *s)
557 statusBar()->setStyleSheet("color: red;");
558 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
559 statusBar()->showMessage(
560 tr("allocation error"));
561 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
562 statusBar()->showMessage(
563 tr("invalid character (use digits, dashes, commas, and spaces)"));
564 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
565 statusBar()->showMessage(
566 tr("overflow"));
567 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
568 statusBar()->showMessage(
569 tr("invalid range (minimum is 6, maximum is 32767)"));
570 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
571 statusBar()->showMessage(
572 tr("overlapping ranges"));
573 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
574 statusBar()->showMessage(
575 tr("values und ranges must be specified in ascending order"));
577 snapping_line->setText(qs);
578 snapping_line->setFocus(Qt::OtherFocusReason);
579 snapping_line->setCursorPosition(s - str.constData());
581 x_height_snapping_exceptions = NULL;
583 else
585 // normalize if there is no error
586 char* new_str = number_set_show(x_height_snapping_exceptions,
587 6, 0x7FFF);
588 snapping_line->setText(new_str);
589 free(new_str);
594 void
595 Main_GUI::check_family_suffix()
597 QString text = family_suffix_line->text();
598 const char* s = qPrintable(text);
600 if (const char* pos = ::check_family_suffix(s))
602 statusBar()->setStyleSheet("color: red;");
603 statusBar()->showMessage(
604 tr("invalid character (use printable ASCII except %()/<>[]{})"));
605 family_suffix_line->setFocus(Qt::OtherFocusReason);
606 family_suffix_line->setCursorPosition(pos - s);
611 void
612 Main_GUI::clear_status_bar()
614 statusBar()->clearMessage();
615 statusBar()->setStyleSheet("");
620 Main_GUI::check_filenames(const QString& input_name,
621 const QString& output_name,
622 const QString& control_name)
624 if (!QFile::exists(input_name))
626 QMessageBox::warning(
627 this,
628 "TTFautohint",
629 tr("The file %1 cannot be found.")
630 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
631 QMessageBox::Ok,
632 QMessageBox::Ok);
633 return 0;
636 if (input_name == output_name)
638 QMessageBox::warning(
639 this,
640 "TTFautohint",
641 tr("Input and output file names must be different."),
642 QMessageBox::Ok,
643 QMessageBox::Ok);
644 return 0;
647 // silently overwrite if watching is active
648 if (QFile::exists(output_name) && !timer->isActive())
650 int ret = QMessageBox::warning(
651 this,
652 "TTFautohint",
653 tr("The file %1 already exists.\n"
654 "Overwrite?")
655 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
656 QMessageBox::Yes | QMessageBox::No,
657 QMessageBox::No);
658 if (ret == QMessageBox::No)
659 return 0;
662 if (!control_name.isEmpty() && !QFile::exists(control_name))
664 QMessageBox::warning(
665 this,
666 "TTFautohint",
667 tr("The file %1 cannot be found.")
668 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name))),
669 QMessageBox::Ok,
670 QMessageBox::Ok);
671 return 0;
674 return 1;
679 Main_GUI::open_files(const QString& input_name,
680 FILE** in,
681 const QString& output_name,
682 FILE** out,
683 const QString& control_name,
684 FILE** control)
686 const int buf_len = 1024;
687 char buf[buf_len];
689 *in = fopen(qPrintable(input_name), "rb");
690 if (!*in)
692 strerror_r(errno, buf, buf_len);
693 QMessageBox::warning(
694 this,
695 "TTFautohint",
696 tr("The following error occurred while opening input file %1:\n")
697 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
698 + QString::fromLocal8Bit(buf),
699 QMessageBox::Ok,
700 QMessageBox::Ok);
701 return 0;
704 *out = fopen(qPrintable(output_name), "wb");
705 if (!*out)
707 strerror_r(errno, buf, buf_len);
708 QMessageBox::warning(
709 this,
710 "TTFautohint",
711 tr("The following error occurred while opening output file %1:\n")
712 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
713 + QString::fromLocal8Bit(buf),
714 QMessageBox::Ok,
715 QMessageBox::Ok);
716 return 0;
719 if (!control_name.isEmpty())
721 *control = fopen(qPrintable(control_name), "r");
722 if (!*control)
724 strerror_r(errno, buf, buf_len);
725 QMessageBox::warning(
726 this,
727 "TTFautohint",
728 tr("The following error occurred"
729 " while opening control instructions file %1:\n")
730 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name)))
731 + QString::fromLocal8Bit(buf),
732 QMessageBox::Ok,
733 QMessageBox::Ok);
734 return 0;
737 else
738 *control = NULL;
740 return 1;
744 void
745 Main_GUI::check_watch()
747 if (!watch_box->isChecked())
748 timer->stop();
749 // the timer gets started in the `run' function
753 void
754 Main_GUI::watch_files()
756 if (fileinfo_input_file.exists()
757 && fileinfo_input_file.isReadable()
758 && fileinfo_control_file.exists()
759 && fileinfo_control_file.isReadable())
761 QDateTime modified_input = fileinfo_input_file.lastModified();
762 QDateTime modified_control = fileinfo_control_file.lastModified();
763 if (modified_input > datetime_input_file
764 || modified_control > datetime_control_file)
765 run(); // this function sets `datetime_XXX'
767 else
768 run(); // let this routine handle all errors
772 extern "C" {
774 struct GUI_Progress_Data
776 long last_sfnt;
777 bool begin;
778 QProgressDialog* dialog;
782 #undef TRDOMAIN
783 #define TRDOMAIN "GuiProgress"
786 gui_progress(long curr_idx,
787 long num_glyphs,
788 long curr_sfnt,
789 long num_sfnts,
790 void* user)
792 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
794 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
796 data->dialog->setLabelText(Tr("Auto-hinting subfont %1 of %2"
797 " with %3 glyphs...")
798 .arg(curr_sfnt + 1)
799 .arg(num_sfnts)
800 .arg(num_glyphs));
802 if (curr_sfnt + 1 == num_sfnts)
804 data->dialog->setAutoReset(true);
805 data->dialog->setAutoClose(true);
807 else
809 data->dialog->setAutoReset(false);
810 data->dialog->setAutoClose(false);
813 data->last_sfnt = curr_sfnt;
814 data->begin = true;
817 if (data->begin)
819 if (num_sfnts == 1)
820 data->dialog->setLabelText(Tr("Auto-hinting %1 glyphs...")
821 .arg(num_glyphs));
822 data->dialog->setMaximum(num_glyphs - 1);
824 data->begin = false;
827 data->dialog->setValue(curr_idx);
829 if (data->dialog->wasCanceled())
830 return 1;
832 return 0;
836 struct GUI_Error_Data
838 Main_GUI* gui;
839 QLocale* locale;
840 QString output_name;
841 QString control_name;
842 int* ignore_restrictions_p;
843 bool retry;
847 #undef TRDOMAIN
848 #define TRDOMAIN "GuiError"
850 void
851 gui_error(TA_Error error,
852 const char* error_string,
853 unsigned int errlinenum,
854 const char* errline,
855 const char* errpos,
856 void* user)
858 GUI_Error_Data* data = static_cast<GUI_Error_Data*>(user);
859 QLocale* locale = data->locale; // for QUOTE_STRING_LITERAL
861 if (!error)
862 return;
864 if (error == TA_Err_Canceled)
866 else if (error == TA_Err_Invalid_FreeType_Version)
867 QMessageBox::critical(
868 data->gui,
869 "TTFautohint",
870 Tr("FreeType version 2.4.5 or higher is needed.\n"
871 "Are you perhaps using a wrong FreeType DLL?"),
872 QMessageBox::Ok,
873 QMessageBox::Ok);
874 else if (error == TA_Err_Invalid_Font_Type)
875 QMessageBox::warning(
876 data->gui,
877 "TTFautohint",
878 Tr("This font is not a valid font"
879 " in SFNT format with TrueType outlines.\n"
880 "In particular, CFF outlines are not supported."),
881 QMessageBox::Ok,
882 QMessageBox::Ok);
883 else if (error == TA_Err_Already_Processed)
884 QMessageBox::warning(
885 data->gui,
886 "TTFautohint",
887 Tr("This font has already been processed by TTFautohint."),
888 QMessageBox::Ok,
889 QMessageBox::Ok);
890 else if (error == TA_Err_Missing_Legal_Permission)
892 int yesno = QMessageBox::warning(
893 data->gui,
894 "TTFautohint",
895 Tr("Bit 1 in the %1 field of the %2 table is set:"
896 " This font must not be modified"
897 " without permission of the legal owner.\n"
898 "Do you have such a permission?")
899 .arg(QUOTE_STRING_LITERAL("fsType"))
900 .arg(QUOTE_STRING_LITERAL("OS/2")),
901 QMessageBox::Yes | QMessageBox::No,
902 QMessageBox::No);
903 if (yesno == QMessageBox::Yes)
905 *data->ignore_restrictions_p = true;
906 data->retry = true;
909 else if (error == TA_Err_Missing_Unicode_CMap)
910 QMessageBox::warning(
911 data->gui,
912 "TTFautohint",
913 Tr("The input font doesn't contain a Unicode character map.\n"
914 "Maybe you haven't set the %1 checkbox?")
915 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
916 QMessageBox::Ok,
917 QMessageBox::Ok);
918 else if (error == TA_Err_Missing_Symbol_CMap)
919 QMessageBox::warning(
920 data->gui,
921 "TTFautohint",
922 Tr("The input font does neither contain a symbol"
923 " nor a character map."),
924 QMessageBox::Ok,
925 QMessageBox::Ok);
926 else if (error == TA_Err_Missing_Glyph)
927 QMessageBox::warning(
928 data->gui,
929 "TTFautohint",
930 Tr("No glyph for a standard character"
931 " to derive standard width and height.\n"
932 "Please check the documentation for a list of"
933 " script-specific standard characters.\n"
934 "\n"
935 "Set the %1 checkbox if you want to circumvent this test.")
936 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
937 QMessageBox::Ok,
938 QMessageBox::Ok);
939 else if (error >= 0x200 && error < 0x300)
940 QMessageBox::warning(
941 data->gui,
942 "TTFautohint",
943 QString::fromLocal8Bit("%1:%2:%3: %4 (0x%5)<br>"
944 "<tt> %6<br>"
945 " %7</tt>")
946 .arg(data->control_name)
947 .arg(errlinenum)
948 .arg(int(errpos - errline + 1))
949 .arg(error_string)
950 .arg(error, 2, 16, QLatin1Char('0'))
951 .arg(errline)
952 .arg('^', int(errpos - errline + 1))
953 .replace(" ", "&nbsp;"),
954 QMessageBox::Ok,
955 QMessageBox::Ok);
956 else
957 QMessageBox::warning(
958 data->gui,
959 "TTFautohint",
960 Tr("Error code 0x%1 while autohinting font:\n")
961 .arg(error, 2, 16, QLatin1Char('0'))
962 + QString::fromLocal8Bit(error_string),
963 QMessageBox::Ok,
964 QMessageBox::Ok);
966 if (QFile::exists(data->output_name)
967 && remove(qPrintable(data->output_name)))
969 const int buf_len = 1024;
970 char buf[buf_len];
972 strerror_r(errno, buf, buf_len);
973 QMessageBox::warning(
974 data->gui,
975 "TTFautohint",
976 Tr("The following error occurred while removing output file %1:\n")
977 .arg(QUOTE_STRING(QDir::toNativeSeparators(data->output_name)))
978 + QString::fromLocal8Bit(buf),
979 QMessageBox::Ok,
980 QMessageBox::Ok);
984 } // extern "C"
987 void
988 Main_GUI::run()
990 statusBar()->clearMessage();
992 QString input_name = QDir::fromNativeSeparators(input_line->text());
993 QString output_name = QDir::fromNativeSeparators(output_line->text());
994 QString control_name = QDir::fromNativeSeparators(control_line->text());
995 if (!check_filenames(input_name, output_name, control_name))
997 timer->stop();
998 return;
1001 // we need C file descriptors for communication with TTF_autohint
1002 FILE* input;
1003 FILE* output;
1004 FILE* control;
1006 again:
1007 if (!open_files(input_name, &input,
1008 output_name, &output,
1009 control_name, &control))
1011 timer->stop();
1012 return;
1015 QProgressDialog dialog;
1016 dialog.setCancelButtonText(tr("Cancel"));
1017 dialog.setMinimumDuration(1000);
1018 dialog.setWindowModality(Qt::WindowModal);
1020 TA_Info_Func info_func = info;
1021 TA_Info_Post_Func info_post_func = info_post;
1022 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
1023 GUI_Error_Data gui_error_data = {this, locale, output_name, control_name,
1024 &ignore_restrictions, false};
1026 fileinfo_input_file.setFile(input_name);
1027 fileinfo_control_file.setFile(control_name);
1029 Info_Data info_data;
1031 info_data.info_string = NULL; // must be deallocated after use
1032 info_data.info_string_wide = NULL; // must be deallocated after use
1033 info_data.info_string_len = 0;
1034 info_data.info_string_wide_len = 0;
1036 info_data.control_name = qPrintable(fileinfo_control_file.fileName());
1038 info_data.hinting_range_min = min_box->value();
1039 info_data.hinting_range_max = max_box->value();
1040 info_data.hinting_limit = no_limit_box->isChecked()
1042 : limit_box->value();
1044 info_data.gray_strong_stem_width = gray_box->isChecked();
1045 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
1046 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
1048 info_data.increase_x_height = no_increase_box->isChecked()
1050 : increase_box->value();
1051 info_data.x_height_snapping_exceptions_string =
1052 qPrintable(x_height_snapping_exceptions_string);
1054 info_data.family_suffix =
1055 family_suffix_line->text().toLocal8Bit().constData();
1056 info_data.family_data_head = NULL;
1058 info_data.fallback_stem_width = default_stem_width_box->isChecked()
1060 : stem_width_box->value();
1062 info_data.windows_compatibility = wincomp_box->isChecked();
1063 info_data.adjust_subglyphs = adjust_box->isChecked();
1064 info_data.hint_composites = hint_box->isChecked();
1065 info_data.symbol = symbol_box->isChecked();
1066 info_data.no_info = info_box->currentIndex() == 0;
1067 info_data.detailed_info = info_box->currentIndex() == 2;
1068 info_data.dehint = dehint_box->isChecked();
1069 info_data.TTFA_info = TTFA_box->isChecked();
1071 strncpy(info_data.default_script,
1072 script_names[default_box->currentIndex()].tag,
1073 sizeof (info_data.default_script));
1074 strncpy(info_data.fallback_script,
1075 script_names[fallback_box->currentIndex()].tag,
1076 sizeof (info_data.fallback_script));
1078 if (info_box->currentIndex())
1080 int ret = build_version_string(&info_data);
1081 if (ret == 1)
1082 QMessageBox::information(
1083 this,
1084 "TTFautohint",
1085 tr("Can't allocate memory for <b>TTFautohint</b> options string"
1086 " in <i>name</i> table."),
1087 QMessageBox::Ok,
1088 QMessageBox::Ok);
1089 else if (ret == 2)
1090 QMessageBox::information(
1091 this,
1092 "TTFautohint",
1093 tr("<b>TTFautohint</b> options string"
1094 " in <i>name</i> table too long."),
1095 QMessageBox::Ok,
1096 QMessageBox::Ok);
1099 if (!*info_data.family_suffix)
1100 info_post_func = NULL;
1102 if (info_data.symbol
1103 && info_data.fallback_stem_width
1104 && !strcmp(info_data.fallback_script, "none"))
1105 QMessageBox::information(
1106 this,
1107 "TTFautohint",
1108 tr("Setting a fallback stem width for a symbol font"
1109 " without setting a fallback script has no effect."),
1110 QMessageBox::Ok,
1111 QMessageBox::Ok);
1113 datetime_input_file = fileinfo_input_file.lastModified();
1114 datetime_control_file = fileinfo_control_file.lastModified();
1116 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
1118 TA_Error error =
1119 TTF_autohint("in-file, out-file, control-file,"
1120 "hinting-range-min, hinting-range-max,"
1121 "hinting-limit,"
1122 "gray-strong-stem-width,"
1123 "gdi-cleartype-strong-stem-width,"
1124 "dw-cleartype-strong-stem-width,"
1125 "progress-callback, progress-callback-data,"
1126 "error-callback, error-callback-data,"
1127 "info-callback, info-post-callback, info-callback-data,"
1128 "ignore-restrictions,"
1129 "windows-compatibility,"
1130 "adjust-subglyphs,"
1131 "hint-composites,"
1132 "increase-x-height,"
1133 "x-height-snapping-exceptions, fallback-stem-width,"
1134 "default-script, fallback-script,"
1135 "symbol, dehint, TTFA-info",
1136 input, output, control,
1137 info_data.hinting_range_min, info_data.hinting_range_max,
1138 info_data.hinting_limit,
1139 info_data.gray_strong_stem_width,
1140 info_data.gdi_cleartype_strong_stem_width,
1141 info_data.dw_cleartype_strong_stem_width,
1142 gui_progress, &gui_progress_data,
1143 gui_error, &gui_error_data,
1144 info_func, info_post_func, &info_data,
1145 ignore_restrictions,
1146 info_data.windows_compatibility,
1147 info_data.adjust_subglyphs,
1148 info_data.hint_composites,
1149 info_data.increase_x_height,
1150 snapping_string.constData(), info_data.fallback_stem_width,
1151 info_data.default_script, info_data.fallback_script,
1152 info_data.symbol, info_data.dehint, info_data.TTFA_info);
1154 if (info_box->currentIndex())
1156 free(info_data.info_string);
1157 free(info_data.info_string_wide);
1160 fclose(input);
1161 fclose(output);
1162 if (control)
1163 fclose(control);
1165 if (error)
1167 // retry if there is a user request to do so (handled in `gui_error')
1168 if (gui_error_data.retry)
1169 goto again;
1171 timer->stop();
1173 else
1175 statusBar()->showMessage(tr("Auto-hinting finished")
1176 + " ("
1177 + QDateTime::currentDateTime()
1178 .toString(Qt::TextDate)
1179 + ").");
1181 // we have successfully processed a file;
1182 // start file watching now if requested
1183 if (watch_box->isChecked())
1184 timer->start();
1189 // XXX distances are specified in pixels,
1190 // making the layout dependent on the output device resolution
1191 void
1192 Main_GUI::create_layout(bool horizontal_layout)
1195 // file stuff
1197 QCompleter* completer = new QCompleter(this);
1198 QFileSystemModel* model = new QFileSystemModel(completer);
1199 model->setRootPath(QDir::rootPath());
1200 completer->setModel(model);
1202 input_label = new QLabel(tr("&Input File:"));
1203 input_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1204 input_button = new QPushButton(tr("Browse..."));
1205 input_label->setBuddy(input_line);
1206 // enforce rich text to get nice word wrapping
1207 input_label->setToolTip(
1208 tr("<b></b>The input file, either a TrueType font (TTF),"
1209 " TrueType collection (TTC), or a TrueType-based OpenType font."));
1210 input_line->setCompleter(completer);
1212 output_label = new QLabel(tr("&Output File:"));
1213 output_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1214 output_button = new QPushButton(tr("Browse..."));
1215 output_label->setBuddy(output_line);
1216 output_label->setToolTip(
1217 tr("<b></b>The output file, which will be essentially identical"
1218 " to the input font but will contain new, generated hints."));
1219 output_line->setCompleter(completer);
1221 control_label = new QLabel(tr("Control Instructions Fil&e:"));
1222 control_line = new Drag_Drop_Line_Edit(DRAG_DROP_ANY);
1223 control_button = new QPushButton(tr("Browse..."));
1224 control_label->setBuddy(control_line);
1225 control_label->setToolTip(
1226 tr("<p>An optional control instructions file to tweak hinting."
1227 " This text file contains entries"
1228 " of one of the following syntax forms"
1229 " (with brackets indicating optional elements).<br>"
1230 "&nbsp;<br>"
1231 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1232 "&nbsp;&nbsp;<i>glyph-id</i>"
1233 "&nbsp;&nbsp;<tt>left</tt>&nbsp;|&nbsp;<tt>right</tt>&nbsp;<i>points</i>"
1234 "&nbsp;&nbsp;[&nbsp;<tt>(</tt><i>left-offset</i><tt>,"
1235 "</tt><i>right-offset</i><tt>)</tt>&nbsp;]<br>"
1236 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1237 "&nbsp;&nbsp;<i>glyph-id</i>"
1238 "&nbsp;&nbsp;<tt>nodir</tt>&nbsp;<i>points</i><br>"
1239 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1240 "&nbsp;&nbsp;<i>glyph-id</i>"
1241 "&nbsp;&nbsp;<tt>touch</tt>&nbsp;|&nbsp;<tt>point</tt>&nbsp;<i>points</i>"
1242 "&nbsp;&nbsp;[&nbsp;<tt>xshift</tt>&nbsp;<i>shift</i>&nbsp;]"
1243 "&nbsp;&nbsp;[&nbsp;<tt>yshift</tt>&nbsp;<i>shift</i>&nbsp;]"
1244 "&nbsp;&nbsp;<tt>@</tt>&nbsp;<i>ppems</i><br>"
1245 "&nbsp;<br>"
1246 "<i>subfont-idx</i> gives the subfont index in a TTC,"
1247 " <i>glyph-id</i> is a glyph name or index.<br>"
1248 "<tt>left</tt> (<tt>right</tt>) creates one-point segments"
1249 " with direction left (right), possibly having a width (in font units)"
1250 " given by <i>left-offset</i> and <i>right-offset</i>"
1251 " relative to the corresponding points.<br>"
1252 "<tt>nodir</tt> removes points from horizontal segments,"
1253 " making them <i>weak</i> points.<br>"
1254 "<tt>touch </tt> (<tt>point</tt>) defines delta exceptions"
1255 " to be applied before (after) the final"
1256 " <tt>IUP</tt> bytecode instructions."
1257 " <tt>touch</tt> also touches points, making them <i>strong</i>."
1258 " In ClearType mode, <tt>point</tt> and <tt>xshift</tt>"
1259 " have no effect.<br>"
1260 "x and y <i>shift</i> values are in the range [-1;1],"
1261 " rounded to multiples of 1/8px.<br>"
1262 "<i>points</i> and <i>ppems</i> are ranges for point indices"
1263 " and ppem values as with x&nbsp;height snapping exceptions.<br>"
1264 "Keywords <tt>left</tt>, <tt>right</tt>, <tt>nodir</tt>,"
1265 " <tt>point</tt>, <tt>touch</tt>, <tt>xshift</tt>, and"
1266 " <tt>yshift</tt> can be abbreviated as <tt>l</tt>, <tt>r</tt>,"
1267 " <tt>n</tt>, <tt>p</tt>, <tt>t</tt>, <tt>x</tt>, and <tt>y</tt>,"
1268 " respectively.<br>"
1269 "Control instruction entries are separated"
1270 " by character&nbsp;<tt>;</tt> or by a newline.<br>"
1271 "A trailing character&nbsp;<tt>\\</tt> continues the current line"
1272 " on the next line.<br>"
1273 "<tt>#</tt> starts a line comment, which gets ignored."
1274 " Empty lines are ignored, too.</p>"
1276 "Examples:<br>"
1277 "&nbsp;&nbsp;<tt>Q left 38 (-70,20)</tt><br>"
1278 "&nbsp;&nbsp;<tt>Adieresis touch 3-6 yshift 0.25 @ 13</tt>"));
1279 control_line->setCompleter(completer);
1282 // minmax controls
1284 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
1285 min_box = new QSpinBox;
1286 min_label->setBuddy(min_box);
1287 min_label->setToolTip(
1288 tr("The minimum PPEM value of the range for which"
1289 " <b>TTFautohint</b> computes <i>hint sets</i>."
1290 " A hint set for a given PPEM value hints this size optimally."
1291 " The larger the range, the more hint sets are considered,"
1292 " usually increasing the size of the bytecode.<br>"
1293 "Note that changing this range doesn't influence"
1294 " the <i>gasp</i> table:"
1295 " Hinting is enabled for all sizes."));
1296 min_box->setKeyboardTracking(false);
1297 min_box->setRange(2, 10000);
1299 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
1300 max_box = new QSpinBox;
1301 max_label->setBuddy(max_box);
1302 max_label->setToolTip(
1303 tr("The maximum PPEM value of the range for which"
1304 " <b>TTFautohint</b> computes <i>hint sets</i>."
1305 " A hint set for a given PPEM value hints this size optimally."
1306 " The larger the range, the more hint sets are considered,"
1307 " usually increasing the size of the bytecode.<br>"
1308 "Note that changing this range doesn't influence"
1309 " the <i>gasp</i> table:"
1310 " Hinting is enabled for all sizes."));
1311 max_box->setKeyboardTracking(false);
1312 max_box->setRange(2, 10000);
1315 // OpenType default script
1317 default_label = new QLabel(tr("Defa&ult Script:"));
1318 default_box = new QComboBox;
1319 default_label->setBuddy(default_box);
1320 default_label->setToolTip(
1321 tr("This sets the default script for OpenType features:"
1322 " After applying all features that are handled specially"
1323 " (for example small caps or superscript glyphs),"
1324 " <b>TTFautohint</b> uses this value for the remaining features."));
1325 for (int i = 0; script_names[i].tag; i++)
1327 // XXX: how to provide translations?
1328 default_box->insertItem(i,
1329 QString("%1 (%2)")
1330 .arg(script_names[i].tag)
1331 .arg(script_names[i].description));
1335 // hinting and fallback controls
1337 fallback_label = new QLabel(tr("Fallback &Script:"));
1338 fallback_box = new QComboBox;
1339 fallback_label->setBuddy(fallback_box);
1340 fallback_label->setToolTip(
1341 tr("This sets the fallback script for glyphs"
1342 " that <b>TTFautohint</b> can't map to a script automatically."));
1343 for (int i = 0; script_names[i].tag; i++)
1345 // XXX: how to provide translations?
1346 fallback_box->insertItem(i,
1347 QString("%1 (%2)")
1348 .arg(script_names[i].tag)
1349 .arg(script_names[i].description));
1353 // hinting limit
1355 limit_label_text_with_key = tr("Hinting &Limit:");
1356 limit_label_text = tr("Hinting Limit:");
1357 limit_label = new QLabel(limit_label_text_with_key);
1358 limit_box = new QSpinBox;
1359 limit_label->setBuddy(limit_box);
1360 limit_label->setToolTip(
1361 tr("Switch off hinting for PPEM values exceeding this limit."
1362 " Changing this value does not influence the size of the bytecode.<br>"
1363 "Note that <b>TTFautohint</b> handles this feature"
1364 " in the output font's bytecode and not in the <i>gasp</i> table."));
1365 limit_box->setKeyboardTracking(false);
1366 limit_box->setRange(2, 10000);
1368 no_limit_box_text_with_key = tr("No Hinting &Limit");
1369 no_limit_box_text = tr("No Hinting Limit");
1370 no_limit_box = new QCheckBox(no_limit_box_text, this);
1371 no_limit_box->setToolTip(
1372 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
1373 " to the bytecode.<br>"
1374 "For testing only."));
1377 // x height increase limit
1379 increase_label_text_with_key = tr("x Height In&crease Limit:");
1380 increase_label_text = tr("x Height Increase Limit:");
1381 increase_label = new QLabel(increase_label_text_with_key);
1382 increase_box = new QSpinBox;
1383 increase_label->setBuddy(increase_box);
1384 increase_label->setToolTip(
1385 tr("For PPEM values in the range 6&nbsp;&le; PPEM &le;&nbsp;<i>n</i>,"
1386 " where <i>n</i> is the value selected by this spin box,"
1387 " round up the font's x&nbsp;height much more often than normally.<br>"
1388 "Use this if holes in letters like <i>e</i> get filled,"
1389 " for example."));
1390 increase_box->setKeyboardTracking(false);
1391 increase_box->setRange(6, 10000);
1393 no_increase_box_text_with_key = tr("No x Height In&crease");
1394 no_increase_box_text = tr("No x Height Increase");
1395 no_increase_box = new QCheckBox(no_increase_box_text, this);
1396 no_increase_box->setToolTip(
1397 tr("If switched on,"
1398 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
1401 // x height snapping exceptions
1403 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
1404 snapping_line = new Tooltip_Line_Edit;
1405 snapping_label->setBuddy(snapping_line);
1406 snapping_label->setToolTip(
1407 tr("<p>A list of comma separated PPEM values or value ranges"
1408 " at which no x&nbsp;height snapping shall be applied"
1409 " (x&nbsp;height snapping usually slightly increases"
1410 " the size of all glyphs).</p>"
1412 "Examples:<br>"
1413 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1414 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1415 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1416 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1419 // family suffix
1421 family_suffix_label = new QLabel(tr("Family Suffix&:"));
1422 family_suffix_line = new Tooltip_Line_Edit;
1423 family_suffix_label->setBuddy(family_suffix_line);
1424 family_suffix_label->setToolTip(
1425 tr("A string that gets appended to the family name entries"
1426 " (name IDs 1, 4, 6, 16, and&nbsp;21)"
1427 " in the font's <i>name</i> table.<br>"
1428 "Mainly for testing purposes, enabling the operating system"
1429 " to display fonts simultaneously that are hinted"
1430 " with different <b>TTFautohint</b> parameters."));
1433 // fallback stem width
1435 stem_width_label_text_with_key = tr("Fallbac&k Stem Width:");
1436 stem_width_label_text = tr("Fallback Stem Width:");
1437 stem_width_label = new QLabel(stem_width_label_text_with_key);
1438 stem_width_box = new QSpinBox;
1439 stem_width_label->setBuddy(stem_width_box);
1440 stem_width_label->setToolTip(
1441 tr("Set horizontal stem width (in font units) for all scripts"
1442 " that lack proper standard characters in the font.<br>"
1443 "If not set, <b>TTFautohint</b> uses a hard-coded default value."));
1444 stem_width_box->setKeyboardTracking(false);
1445 stem_width_box->setRange(1, 10000);
1447 default_stem_width_box_text_with_key = tr("Default Fallbac&k Stem Width");
1448 default_stem_width_box_text = tr("Default Fallback Stem Width");
1449 default_stem_width_box = new QCheckBox(default_stem_width_box_text, this);
1450 default_stem_width_box->setToolTip(
1451 tr("If switched on, <b>TTFautohint</b> uses a default value"
1452 " for the fallback stem width (50 font units at 2048 UPEM)."));
1455 // flags
1457 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1458 wincomp_box->setToolTip(
1459 tr("If switched on, add two artificial blue zones positioned at the"
1460 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1461 " (from the font's <i>OS/2</i> table)."
1462 " This option, usually in combination"
1463 " with value <tt>-</tt> (a single dash)"
1464 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1465 " should be used if those two <i>OS/2</i> values are tight,"
1466 " and you are experiencing clipping during rendering."));
1468 adjust_box = new QCheckBox(tr("Ad&just Subglyphs"), this);
1469 adjust_box->setToolTip(
1470 tr("If switched on, the original bytecode of the input font"
1471 " gets applied (at EM size, usually 2048ppem)"
1472 " to derive the glyph outlines for <b>TTFautohint</b>.<br>"
1473 "Use this option only if subglyphs"
1474 " are incorrectly scaled and shifted.<br>"
1475 "Note that the original bytecode will always be discarded."));
1477 hint_box = new QCheckBox(tr("Hint Co&mposites")
1478 + " ", this); // make label wider
1479 hint_box->setToolTip(
1480 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1481 " as a whole, including subglyphs."
1482 " Otherwise, glyph components get hinted separately.<br>"
1483 "Deactivating this flag reduces the bytecode size enormously,"
1484 " however, it might yield worse results."));
1486 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1487 symbol_box->setToolTip(
1488 tr("If switched on, <b>TTFautohint</b> accepts fonts"
1489 " that don't contain a single standard character"
1490 " for any of the supported scripts.<br>"
1491 "Use this for symbol or dingbat fonts, for example."));
1493 dehint_box = new QCheckBox(tr("&Dehint"), this);
1494 dehint_box->setToolTip(
1495 tr("If set, remove all hints from the font.<br>"
1496 "For testing only."));
1498 info_label = new QLabel(tr("ttf&autohint Info:"));
1499 info_box = new QComboBox;
1500 info_label->setBuddy(info_box);
1501 info_label->setToolTip(
1502 tr("Specify which information about <b>TTFautohint</b>"
1503 " and its calling parameters gets added to the version string(s)"
1504 " (name ID&nbsp;5) in the <i>name</i> table."));
1505 info_box->insertItem(0, tr("None"));
1506 info_box->insertItem(1, tr("Version"));
1507 info_box->insertItem(2, tr("Version and Parameters"));
1509 TTFA_box = new QCheckBox(tr("Add TTFA Info Ta&ble"), this);
1510 TTFA_box->setToolTip(
1511 tr("If switched on, an SFNT table called <i>TTFA</i>"
1512 " gets added to the output font,"
1513 " holding a dump of all parameters."
1514 " In particular, it lists all control instructions."));
1517 // stem width and positioning
1519 stem_label = new QLabel(tr("Stron&g Stem Width and Positioning:"));
1520 stem_label->setToolTip(
1521 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1522 " that can be selected for various hinting modes."
1524 "<p><i>strong</i> (checkbox set):"
1525 " Position horizontal stems and snap stem widths"
1526 " to integer pixel values. While making the output look crisper,"
1527 " outlines become more distorted.</p>"
1529 "<p><i>smooth</i> (checkbox not set):"
1530 " Use discrete values for horizontal stems and stem widths."
1531 " This only slightly increases the contrast"
1532 " but avoids larger outline distortion.</p>"));
1534 gray_box = new QCheckBox(tr("Grayscale"), this);
1535 gray_box->setToolTip(
1536 tr("<b></b>Grayscale rendering, no ClearType activated."));
1537 stem_label->setBuddy(gray_box);
1539 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1540 gdi_box->setToolTip(
1541 tr("GDI ClearType rendering,"
1542 " introduced in 2000 for Windows XP.<br>"
1543 "The rasterizer version (as returned by the"
1544 " GETINFO bytecode instruction) is in the range"
1545 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1546 "Along the vertical axis, this mode behaves like B/W rendering."));
1548 dw_box = new QCheckBox(tr("DW ClearType"), this);
1549 dw_box->setToolTip(
1550 tr("DirectWrite ClearType rendering,"
1551 " introduced in 2008 for Windows Vista.<br>"
1552 "The rasterizer version (as returned by the"
1553 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1554 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1555 "Smooth rendering along the vertical axis."));
1558 // running
1560 watch_box = new QCheckBox(tr("&Watch Input Files"), this);
1561 watch_box->setToolTip(
1562 tr("If switched on, <b>TTFautohint</b> automatically re-runs"
1563 " the hinting process as soon as an input file"
1564 " (either the font or the control instructions file) is modified.<br>"
1565 "Pressing the %1 button starts watching.<br>"
1566 "If an error occurs, watching stops and must be restarted"
1567 " with the %1 button.")
1568 .arg(QUOTE_STRING_LITERAL(tr("Run"))));
1570 run_button = new QPushButton(" "
1571 + tr("&Run")
1572 + " "); // make label wider
1574 if (horizontal_layout)
1575 create_horizontal_layout();
1576 else
1577 create_vertical_layout();
1581 // XXX distances are specified in pixels,
1582 // making the layout dependent on the output device resolution
1583 void
1584 Main_GUI::create_vertical_layout()
1586 // top area
1587 QGridLayout* file_layout = new QGridLayout;
1589 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1590 file_layout->addWidget(input_line, 0, 1);
1591 file_layout->addWidget(input_button, 0, 2);
1593 file_layout->setRowStretch(1, 1);
1595 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1596 file_layout->addWidget(output_line, 2, 1);
1597 file_layout->addWidget(output_button, 2, 2);
1599 file_layout->setRowStretch(3, 1);
1601 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1602 file_layout->addWidget(control_line, 4, 1);
1603 file_layout->addWidget(control_button, 4, 2);
1605 // bottom area
1606 QGridLayout* run_layout = new QGridLayout;
1608 run_layout->addWidget(watch_box, 0, 1);
1609 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1610 run_layout->setColumnStretch(0, 1);
1611 run_layout->setColumnStretch(2, 2);
1614 // the whole gui
1616 QGridLayout* gui_layout = new QGridLayout;
1617 QFrame* hline = new QFrame;
1618 hline->setFrameShape(QFrame::HLine);
1619 int row = 0; // this counter simplifies inserting new items
1621 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1622 gui_layout->setRowStretch(row++, 1);
1624 gui_layout->addLayout(file_layout, row, 0, row, -1);
1625 gui_layout->setRowStretch(row++, 1);
1627 gui_layout->addWidget(hline, row, 0, row, -1);
1628 gui_layout->setRowStretch(row++, 1);
1630 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1631 gui_layout->setRowStretch(row++, 1);
1633 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1634 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1635 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1636 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1638 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1639 gui_layout->setRowStretch(row++, 1);
1641 gui_layout->addWidget(default_label, row, 0, Qt::AlignRight);
1642 gui_layout->addWidget(default_box, row++, 1, Qt::AlignLeft);
1643 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1644 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1646 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1647 gui_layout->setRowStretch(row++, 1);
1649 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1650 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1651 gui_layout->addWidget(no_limit_box, row++, 1);
1653 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1654 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1655 gui_layout->addWidget(no_increase_box, row++, 1);
1657 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1658 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1660 gui_layout->addWidget(stem_width_label, row, 0, Qt::AlignRight);
1661 gui_layout->addWidget(stem_width_box, row++, 1, Qt::AlignLeft);
1662 gui_layout->addWidget(default_stem_width_box, row++, 1);
1664 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1665 gui_layout->setRowStretch(row++, 1);
1667 gui_layout->addWidget(wincomp_box, row++, 1);
1668 gui_layout->addWidget(adjust_box, row++, 1);
1669 gui_layout->addWidget(hint_box, row++, 1);
1670 gui_layout->addWidget(symbol_box, row++, 1);
1671 gui_layout->addWidget(dehint_box, row++, 1);
1673 gui_layout->addWidget(info_label, row, 0, Qt::AlignRight);
1674 gui_layout->addWidget(info_box, row++, 1, Qt::AlignLeft);
1675 gui_layout->addWidget(TTFA_box, row++, 1);
1676 gui_layout->addWidget(family_suffix_label, row, 0, Qt::AlignRight);
1677 gui_layout->addWidget(family_suffix_line, row++, 1, Qt::AlignLeft);
1679 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1680 gui_layout->setRowStretch(row++, 1);
1682 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1683 gui_layout->addWidget(gray_box, row++, 1);
1684 gui_layout->addWidget(gdi_box, row++, 1);
1685 gui_layout->addWidget(dw_box, row++, 1);
1687 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1688 gui_layout->setRowStretch(row++, 1);
1690 gui_layout->addLayout(run_layout, row, 0, row, -1);
1692 // create dummy widget to register layout
1693 QWidget* main_widget = new QWidget;
1694 main_widget->setLayout(gui_layout);
1695 setCentralWidget(main_widget);
1696 setWindowTitle("TTFautohint");
1700 // XXX distances are specified in pixels,
1701 // making the layout dependent on the output device resolution
1702 void
1703 Main_GUI::create_horizontal_layout()
1705 // top area
1706 QGridLayout* file_layout = new QGridLayout;
1708 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1709 file_layout->addWidget(input_line, 0, 1);
1710 file_layout->addWidget(input_button, 0, 2);
1712 file_layout->setRowStretch(1, 1);
1714 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1715 file_layout->addWidget(output_line, 2, 1);
1716 file_layout->addWidget(output_button, 2, 2);
1718 file_layout->setRowStretch(3, 1);
1720 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1721 file_layout->addWidget(control_line, 4, 1);
1722 file_layout->addWidget(control_button, 4, 2);
1724 // bottom area
1725 QGridLayout* run_layout = new QGridLayout;
1727 run_layout->addWidget(watch_box, 0, 1);
1728 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1729 run_layout->setColumnStretch(0, 2);
1730 run_layout->setColumnStretch(2, 3);
1731 run_layout->setColumnStretch(4, 1);
1734 // the whole gui
1736 QGridLayout* gui_layout = new QGridLayout;
1737 QFrame* hline = new QFrame;
1738 hline->setFrameShape(QFrame::HLine);
1739 int row = 0; // this counter simplifies inserting new items
1741 // margin
1742 gui_layout->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1743 gui_layout->setColumnStretch(0, 1);
1745 // left
1746 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1747 gui_layout->setRowStretch(row++, 1);
1749 gui_layout->addLayout(file_layout, row, 0, row, -1);
1750 gui_layout->setRowStretch(row++, 1);
1752 gui_layout->addWidget(hline, row, 0, row, -1);
1753 gui_layout->setRowStretch(row++, 1);
1755 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1756 gui_layout->setRowStretch(row++, 1);
1758 gui_layout->addWidget(min_label, row, 1, Qt::AlignRight);
1759 gui_layout->addWidget(min_box, row++, 2, Qt::AlignLeft);
1760 gui_layout->addWidget(max_label, row, 1, Qt::AlignRight);
1761 gui_layout->addWidget(max_box, row++, 2, Qt::AlignLeft);
1763 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1764 gui_layout->setRowStretch(row++, 1);
1766 gui_layout->addWidget(default_label, row, 1, Qt::AlignRight);
1767 gui_layout->addWidget(default_box, row++, 2, Qt::AlignLeft);
1768 gui_layout->addWidget(fallback_label, row, 1, Qt::AlignRight);
1769 gui_layout->addWidget(fallback_box, row++, 2, Qt::AlignLeft);
1771 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1772 gui_layout->setRowStretch(row++, 1);
1774 gui_layout->addWidget(limit_label, row, 1, Qt::AlignRight);
1775 gui_layout->addWidget(limit_box, row++, 2, Qt::AlignLeft);
1776 gui_layout->addWidget(no_limit_box, row++, 2);
1778 gui_layout->addWidget(increase_label, row, 1, Qt::AlignRight);
1779 gui_layout->addWidget(increase_box, row++, 2, Qt::AlignLeft);
1780 gui_layout->addWidget(no_increase_box, row++, 2);
1782 gui_layout->addWidget(snapping_label, row, 1, Qt::AlignRight);
1783 gui_layout->addWidget(snapping_line, row++, 2, Qt::AlignLeft);
1785 gui_layout->addWidget(stem_width_label, row, 1, Qt::AlignRight);
1786 gui_layout->addWidget(stem_width_box, row++, 2, Qt::AlignLeft);
1787 gui_layout->addWidget(default_stem_width_box, row++, 2);
1789 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1790 gui_layout->setRowStretch(row++, 1);
1792 gui_layout->addLayout(run_layout, row, 0, row, -1);
1794 // column separator
1795 gui_layout->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1796 gui_layout->setColumnStretch(3, 1);
1798 // right
1799 row = 4;
1800 gui_layout->addWidget(wincomp_box, row++, 4);
1801 gui_layout->addWidget(adjust_box, row++, 4);
1802 gui_layout->addWidget(hint_box, row++, 4);
1803 gui_layout->addWidget(symbol_box, row++, 4);
1804 gui_layout->addWidget(dehint_box, row++, 4);
1806 gui_layout->addWidget(info_label, row, 3, Qt::AlignRight);
1807 gui_layout->addWidget(info_box, row++, 4, Qt::AlignLeft);
1808 gui_layout->addWidget(TTFA_box, row++, 4);
1809 gui_layout->addWidget(family_suffix_label, row, 3, Qt::AlignRight);
1810 gui_layout->addWidget(family_suffix_line, row++, 4, Qt::AlignLeft);
1812 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1813 gui_layout->setRowStretch(row++, 1);
1815 gui_layout->addWidget(stem_label, row++, 4);
1817 QGridLayout* stem_layout = new QGridLayout;
1818 stem_layout->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1819 stem_layout->addWidget(gray_box, 0, 1);
1820 stem_layout->addWidget(gdi_box, 1, 1);
1821 stem_layout->addWidget(dw_box, 2, 1);
1823 gui_layout->addLayout(stem_layout, row, 4, 3, 1);
1824 row += 3;
1826 // margin
1827 gui_layout->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1828 gui_layout->setColumnStretch(5, 1);
1830 // create dummy widget to register layout
1831 QWidget* main_widget = new QWidget;
1832 main_widget->setLayout(gui_layout);
1833 setCentralWidget(main_widget);
1834 setWindowTitle("TTFautohint");
1838 void
1839 Main_GUI::create_connections()
1841 connect(input_button, SIGNAL(clicked()), this,
1842 SLOT(browse_input()));
1843 connect(output_button, SIGNAL(clicked()), this,
1844 SLOT(browse_output()));
1845 connect(control_button, SIGNAL(clicked()), this,
1846 SLOT(browse_control()));
1848 connect(input_line, SIGNAL(textChanged(QString)), this,
1849 SLOT(check_run()));
1850 connect(output_line, SIGNAL(textChanged(QString)), this,
1851 SLOT(check_run()));
1853 connect(input_line, SIGNAL(editingFinished()), this,
1854 SLOT(absolute_input()));
1855 connect(output_line, SIGNAL(editingFinished()), this,
1856 SLOT(absolute_output()));
1857 connect(control_line, SIGNAL(editingFinished()), this,
1858 SLOT(absolute_control()));
1860 connect(min_box, SIGNAL(valueChanged(int)), this,
1861 SLOT(check_min()));
1862 connect(max_box, SIGNAL(valueChanged(int)), this,
1863 SLOT(check_max()));
1865 connect(limit_box, SIGNAL(valueChanged(int)), this,
1866 SLOT(check_limit()));
1867 connect(no_limit_box, SIGNAL(clicked()), this,
1868 SLOT(check_no_limit()));
1870 connect(no_increase_box, SIGNAL(clicked()), this,
1871 SLOT(check_no_increase()));
1873 connect(snapping_line, SIGNAL(editingFinished()), this,
1874 SLOT(check_number_set()));
1875 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1876 SLOT(clear_status_bar()));
1878 connect(family_suffix_line, SIGNAL(editingFinished()), this,
1879 SLOT(check_family_suffix()));
1880 connect(family_suffix_line, SIGNAL(textEdited(QString)), this,
1881 SLOT(clear_status_bar()));
1883 connect(default_stem_width_box, SIGNAL(clicked()), this,
1884 SLOT(check_default_stem_width()));
1886 connect(dehint_box, SIGNAL(clicked()), this,
1887 SLOT(check_dehint()));
1889 connect(timer, SIGNAL(timeout()), this,
1890 SLOT(watch_files()));
1892 connect(watch_box, SIGNAL(clicked()), this,
1893 SLOT(check_watch()));
1895 connect(run_button, SIGNAL(clicked()), this,
1896 SLOT(run()));
1900 void
1901 Main_GUI::create_actions()
1903 exit_act = new QAction(tr("E&xit"), this);
1904 exit_act->setShortcuts(QKeySequence::Quit);
1905 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1907 about_act = new QAction(tr("&About"), this);
1908 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1910 about_Qt_act = new QAction(tr("About &Qt"), this);
1911 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1915 void
1916 Main_GUI::create_menus()
1918 file_menu = menuBar()->addMenu(tr("&File"));
1919 file_menu->addAction(exit_act);
1921 help_menu = menuBar()->addMenu(tr("&Help"));
1922 help_menu->addAction(about_act);
1923 help_menu->addAction(about_Qt_act);
1927 void
1928 Main_GUI::create_status_bar()
1930 statusBar()->showMessage("");
1934 void
1935 Main_GUI::set_defaults()
1937 min_box->setValue(hinting_range_min);
1938 max_box->setValue(hinting_range_max);
1940 default_box->setCurrentIndex(default_script_idx);
1941 fallback_box->setCurrentIndex(fallback_script_idx);
1943 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1944 // handle command line option `--hinting-limit=0'
1945 if (!hinting_limit)
1947 hinting_limit = max_box->value();
1948 no_limit_box->setChecked(true);
1951 increase_box->setValue(increase_x_height ? increase_x_height
1952 : TA_INCREASE_X_HEIGHT);
1953 // handle command line option `--increase-x-height=0'
1954 if (!increase_x_height)
1956 increase_x_height = TA_INCREASE_X_HEIGHT;
1957 no_increase_box->setChecked(true);
1960 snapping_line->setText(x_height_snapping_exceptions_string);
1961 family_suffix_line->setText(family_suffix);
1963 if (fallback_stem_width)
1964 stem_width_box->setValue(fallback_stem_width);
1965 else
1967 stem_width_box->setValue(50);
1968 default_stem_width_box->setChecked(true);
1971 if (windows_compatibility)
1972 wincomp_box->setChecked(true);
1973 if (adjust_subglyphs)
1974 adjust_box->setChecked(true);
1975 if (hint_composites)
1976 hint_box->setChecked(true);
1977 if (symbol)
1978 symbol_box->setChecked(true);
1979 if (dehint)
1980 dehint_box->setChecked(true);
1981 if (no_info)
1982 info_box->setCurrentIndex(0);
1983 else if (detailed_info)
1984 info_box->setCurrentIndex(2);
1985 else
1986 info_box->setCurrentIndex(1);
1987 if (TTFA_info)
1988 TTFA_box->setChecked(true);
1990 if (gray_strong_stem_width)
1991 gray_box->setChecked(true);
1992 if (gdi_cleartype_strong_stem_width)
1993 gdi_box->setChecked(true);
1994 if (dw_cleartype_strong_stem_width)
1995 dw_box->setChecked(true);
1997 run_button->setEnabled(false);
1999 check_min();
2000 check_max();
2001 check_limit();
2003 check_no_limit();
2004 check_no_increase();
2005 check_number_set();
2007 // do this last since it disables almost everything
2008 check_dehint();
2012 void
2013 Main_GUI::read_settings()
2015 QSettings settings;
2016 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
2017 // QSize size = settings.value("size", QSize(400, 400)).toSize();
2018 // resize(size);
2019 // move(pos);
2023 void
2024 Main_GUI::write_settings()
2026 QSettings settings;
2027 // settings.setValue("pos", pos());
2028 // settings.setValue("size", size());
2031 // end of maingui.cpp