Document forced point directions.
[ttfautohint.git] / frontend / maingui.cpp
blob2dcac3f25692264e8a26414c4957acee1ca362fb
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 Comp&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 const char* dflt,
108 const char* fallback,
109 bool symb,
110 bool dh,
111 bool TTFA)
112 : hinting_range_min(range_min),
113 hinting_range_max(range_max),
114 hinting_limit(limit),
115 gray_strong_stem_width(gray),
116 gdi_cleartype_strong_stem_width(gdi),
117 dw_cleartype_strong_stem_width(dw),
118 increase_x_height(increase),
119 x_height_snapping_exceptions_string(exceptions),
120 fallback_stem_width(stem_width),
121 ignore_restrictions(ignore),
122 windows_compatibility(wincomp),
123 adjust_subglyphs(adjust),
124 hint_composites(composites),
125 no_info(no),
126 symbol(symb),
127 dehint(dh),
128 TTFA_info(TTFA)
130 int i;
132 // map default script tag to an index,
133 // replacing an invalid one with the default value
134 int latn_script_idx = 0;
135 for (i = 0; script_names[i].tag; i++)
137 if (!strcmp("latn", script_names[i].tag))
138 latn_script_idx = i;
139 if (!strcmp(dflt, script_names[i].tag))
140 break;
142 default_script_idx = script_names[i].tag ? i : latn_script_idx;
144 // map fallback script tag to an index,
145 // replacing an invalid one with the default value
146 int none_script_idx = 0;
147 for (i = 0; script_names[i].tag; i++)
149 if (!strcmp("none", script_names[i].tag))
150 none_script_idx = i;
151 if (!strcmp(fallback, script_names[i].tag))
152 break;
154 fallback_script_idx = script_names[i].tag ? i : none_script_idx;
156 x_height_snapping_exceptions = NULL;
158 // if file watching is active, we regularly poll the file status
159 timer = new QTimer(this);
160 timer->setInterval(1000);
161 fileinfo_input_file.setCaching(false);
162 fileinfo_control_file.setCaching(false);
164 // XXX register translations somewhere and loop over them
165 if (QLocale::system().name() == "en_US")
166 locale = new QLocale;
167 else
168 locale = new QLocale(QLocale::C);
170 // For real numbers (both parsing and displaying) we only use `.' as the
171 // decimal separator; similarly, we don't want localized formats like a
172 // thousands separator for any number.
173 setlocale(LC_NUMERIC, "C");
175 create_layout(horizontal_layout);
176 create_connections();
177 create_actions();
178 create_menus();
179 create_status_bar();
181 set_defaults();
182 read_settings();
184 setUnifiedTitleAndToolBarOnMac(true);
188 Main_GUI::~Main_GUI()
190 number_set_free(x_height_snapping_exceptions);
194 // overloading
196 void
197 Main_GUI::closeEvent(QCloseEvent* event)
199 write_settings();
200 event->accept();
204 void
205 Main_GUI::about()
207 QMessageBox::about(this,
208 tr("About TTFautohint"),
209 tr("<p>This is <b>TTFautohint</b> version %1<br>"
210 " Copyright %2 2011-2014<br>"
211 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
213 "<p><b>TTFautohint</b> adds new auto-generated hints"
214 " to a TrueType font or TrueType collection.</p>"
216 "<p>License:"
217 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT'>FreeType"
218 " License (FTL)</a> or"
219 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT'>GNU"
220 " GPLv2</a></p>")
221 .arg(VERSION)
222 .arg(QChar(0xA9)));
226 void
227 Main_GUI::browse_input()
229 // XXX remember last directory
230 QString file = QFileDialog::getOpenFileName(
231 this,
232 tr("Open Input File"),
233 QDir::homePath(),
234 "");
235 if (!file.isEmpty())
236 input_line->setText(QDir::toNativeSeparators(file));
240 void
241 Main_GUI::browse_output()
243 // XXX remember last directory
244 QString file = QFileDialog::getSaveFileName(
245 this,
246 tr("Open Output File"),
247 QDir::homePath(),
248 "");
250 if (!file.isEmpty())
251 output_line->setText(QDir::toNativeSeparators(file));
255 void
256 Main_GUI::browse_control()
258 // XXX remember last directory
259 QString file = QFileDialog::getOpenFileName(
260 this,
261 tr("Open Control INstructions File"),
262 QDir::homePath(),
263 "");
265 if (!file.isEmpty())
266 control_line->setText(QDir::toNativeSeparators(file));
270 void
271 Main_GUI::check_min()
273 int min = min_box->value();
274 int max = max_box->value();
275 int limit = limit_box->value();
276 if (min > max)
277 max_box->setValue(min);
278 if (min > limit)
279 limit_box->setValue(min);
283 void
284 Main_GUI::check_max()
286 int min = min_box->value();
287 int max = max_box->value();
288 int limit = limit_box->value();
289 if (max < min)
290 min_box->setValue(max);
291 if (max > limit)
292 limit_box->setValue(max);
296 void
297 Main_GUI::check_limit()
299 int min = min_box->value();
300 int max = max_box->value();
301 int limit = limit_box->value();
302 if (limit < max)
303 max_box->setValue(limit);
304 if (limit < min)
305 min_box->setValue(limit);
309 void
310 Main_GUI::check_dehint()
312 if (dehint_box->isChecked())
314 min_label->setEnabled(false);
315 min_box->setEnabled(false);
317 max_label->setEnabled(false);
318 max_box->setEnabled(false);
320 default_label->setEnabled(false);
321 default_box->setEnabled(false);
322 fallback_label->setEnabled(false);
323 fallback_box->setEnabled(false);
325 no_limit_box->setEnabled(false);
326 limit_label->setEnabled(false);
327 limit_box->setEnabled(false);
329 no_increase_box->setEnabled(false);
330 increase_label->setEnabled(false);
331 increase_box->setEnabled(false);
333 snapping_label->setEnabled(false);
334 snapping_line->setEnabled(false);
336 default_stem_width_box->setEnabled(false);
337 stem_width_label->setEnabled(false);
338 stem_width_box->setEnabled(false);
340 wincomp_box->setEnabled(false);
341 adjust_box->setEnabled(false);
342 hint_box->setEnabled(false);
343 symbol_box->setEnabled(false);
345 stem_label->setEnabled(false);
346 gray_box->setEnabled(false);
347 gdi_box->setEnabled(false);
348 dw_box->setEnabled(false);
350 else
352 min_label->setEnabled(true);
353 min_box->setEnabled(true);
355 max_label->setEnabled(true);
356 max_box->setEnabled(true);
358 default_label->setEnabled(true);
359 default_box->setEnabled(true);
360 fallback_label->setEnabled(true);
361 fallback_box->setEnabled(true);
363 no_limit_box->setEnabled(true);
364 check_no_limit();
366 no_increase_box->setEnabled(true);
367 check_no_increase();
369 snapping_label->setEnabled(true);
370 snapping_line->setEnabled(true);
372 default_stem_width_box->setEnabled(true);
373 check_default_stem_width();
375 wincomp_box->setEnabled(true);
376 adjust_box->setEnabled(true);
377 hint_box->setEnabled(true);
378 symbol_box->setEnabled(true);
380 stem_label->setEnabled(true);
381 gray_box->setEnabled(true);
382 gdi_box->setEnabled(true);
383 dw_box->setEnabled(true);
388 void
389 Main_GUI::check_no_limit()
391 if (no_limit_box->isChecked())
393 limit_label->setEnabled(false);
394 limit_label->setText(limit_label_text);
395 limit_box->setEnabled(false);
396 no_limit_box->setText(no_limit_box_text_with_key);
398 else
400 limit_label->setEnabled(true);
401 limit_label->setText(limit_label_text_with_key);
402 limit_box->setEnabled(true);
403 no_limit_box->setText(no_limit_box_text);
408 void
409 Main_GUI::check_no_increase()
411 if (no_increase_box->isChecked())
413 increase_label->setEnabled(false);
414 increase_label->setText(increase_label_text);
415 increase_box->setEnabled(false);
416 no_increase_box->setText(no_increase_box_text_with_key);
418 else
420 increase_label->setEnabled(true);
421 increase_label->setText(increase_label_text_with_key);
422 increase_box->setEnabled(true);
423 no_increase_box->setText(no_increase_box_text);
428 void
429 Main_GUI::check_default_stem_width()
431 if (default_stem_width_box->isChecked())
433 stem_width_label->setEnabled(false);
434 stem_width_label->setText(stem_width_label_text);
435 stem_width_box->setEnabled(false);
436 default_stem_width_box->setText(default_stem_width_box_text_with_key);
438 else
440 stem_width_label->setEnabled(true);
441 stem_width_label->setText(stem_width_label_text_with_key);
442 stem_width_box->setEnabled(true);
443 default_stem_width_box->setText(default_stem_width_box_text);
448 void
449 Main_GUI::check_run()
451 if (input_line->text().isEmpty() || output_line->text().isEmpty())
452 run_button->setEnabled(false);
453 else
454 run_button->setEnabled(true);
458 void
459 Main_GUI::absolute_input()
461 QString input_name = QDir::fromNativeSeparators(input_line->text());
462 if (!input_name.isEmpty()
463 && QDir::isRelativePath(input_name))
465 QDir cur_path(QDir::currentPath() + "/" + input_name);
466 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
471 void
472 Main_GUI::absolute_output()
474 QString output_name = QDir::fromNativeSeparators(output_line->text());
475 if (!output_name.isEmpty()
476 && QDir::isRelativePath(output_name))
478 QDir cur_path(QDir::currentPath() + "/" + output_name);
479 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
484 void
485 Main_GUI::absolute_control()
487 QString control_name = QDir::fromNativeSeparators(control_line->text());
488 if (!control_name.isEmpty()
489 && QDir::isRelativePath(control_name))
491 QDir cur_path(QDir::currentPath() + "/" + control_name);
492 control_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
497 void
498 Main_GUI::check_number_set()
500 QString text = snapping_line->text();
501 QString qs;
503 // construct ASCII string from arbitrary Unicode data;
504 // the idea is to accept, say, CJK fullwidth digits also
505 for (int i = 0; i < text.size(); i++)
507 QChar c = text.at(i);
509 int digit = c.digitValue();
510 if (digit >= 0)
511 qs += QString::number(digit);
512 else if (c.isSpace())
513 qs += ' ';
514 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
515 // to the `-' key in some Japanese input methods
516 else if (c.category() == QChar::Punctuation_Dash
517 || c == QChar(0x30FC))
518 qs += '-';
519 // various Unicode COMMA characters,
520 // including representation forms
521 else if (c == QChar(',')
522 || c == QChar(0x055D)
523 || c == QChar(0x060C)
524 || c == QChar(0x07F8)
525 || c == QChar(0x1363)
526 || c == QChar(0x1802)
527 || c == QChar(0x1808)
528 || c == QChar(0x3001)
529 || c == QChar(0xA4FE)
530 || c == QChar(0xA60D)
531 || c == QChar(0xA6F5)
532 || c == QChar(0xFE10)
533 || c == QChar(0xFE11)
534 || c == QChar(0xFE50)
535 || c == QChar(0xFE51)
536 || c == QChar(0xFF0C)
537 || c == QChar(0xFF64))
538 qs += ',';
539 else
540 qs += c; // we do error handling below
543 if (x_height_snapping_exceptions)
544 number_set_free(x_height_snapping_exceptions);
546 QByteArray str = qs.toLocal8Bit();
547 const char* s = number_set_parse(str.constData(),
548 &x_height_snapping_exceptions,
549 6, 0x7FFF);
550 if (s && *s)
552 statusBar()->setStyleSheet("color: red;");
553 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
554 statusBar()->showMessage(
555 tr("allocation error"));
556 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
557 statusBar()->showMessage(
558 tr("invalid character (use digits, dashes, commas, and spaces)"));
559 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
560 statusBar()->showMessage(
561 tr("overflow"));
562 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
563 statusBar()->showMessage(
564 tr("invalid range (minimum is 6, maximum is 32767)"));
565 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
566 statusBar()->showMessage(
567 tr("overlapping ranges"));
568 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
569 statusBar()->showMessage(
570 tr("values und ranges must be specified in ascending order"));
572 snapping_line->setText(qs);
573 snapping_line->setFocus(Qt::OtherFocusReason);
574 snapping_line->setCursorPosition(s - str.constData());
576 x_height_snapping_exceptions = NULL;
578 else
580 // normalize if there is no error
581 char* new_str = number_set_show(x_height_snapping_exceptions,
582 6, 0x7FFF);
583 snapping_line->setText(new_str);
584 free(new_str);
589 void
590 Main_GUI::clear_status_bar()
592 statusBar()->clearMessage();
593 statusBar()->setStyleSheet("");
598 Main_GUI::check_filenames(const QString& input_name,
599 const QString& output_name,
600 const QString& control_name)
602 if (!QFile::exists(input_name))
604 QMessageBox::warning(
605 this,
606 "TTFautohint",
607 tr("The file %1 cannot be found.")
608 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
609 QMessageBox::Ok,
610 QMessageBox::Ok);
611 return 0;
614 if (input_name == output_name)
616 QMessageBox::warning(
617 this,
618 "TTFautohint",
619 tr("Input and output file names must be different."),
620 QMessageBox::Ok,
621 QMessageBox::Ok);
622 return 0;
625 // silently overwrite if watching is active
626 if (QFile::exists(output_name) && !timer->isActive())
628 int ret = QMessageBox::warning(
629 this,
630 "TTFautohint",
631 tr("The file %1 already exists.\n"
632 "Overwrite?")
633 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
634 QMessageBox::Yes | QMessageBox::No,
635 QMessageBox::No);
636 if (ret == QMessageBox::No)
637 return 0;
640 if (!control_name.isEmpty() && !QFile::exists(control_name))
642 QMessageBox::warning(
643 this,
644 "TTFautohint",
645 tr("The file %1 cannot be found.")
646 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name))),
647 QMessageBox::Ok,
648 QMessageBox::Ok);
649 return 0;
652 return 1;
657 Main_GUI::open_files(const QString& input_name,
658 FILE** in,
659 const QString& output_name,
660 FILE** out,
661 const QString& control_name,
662 FILE** control)
664 const int buf_len = 1024;
665 char buf[buf_len];
667 *in = fopen(qPrintable(input_name), "rb");
668 if (!*in)
670 strerror_r(errno, buf, buf_len);
671 QMessageBox::warning(
672 this,
673 "TTFautohint",
674 tr("The following error occurred while opening input file %1:\n")
675 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
676 + QString::fromLocal8Bit(buf),
677 QMessageBox::Ok,
678 QMessageBox::Ok);
679 return 0;
682 *out = fopen(qPrintable(output_name), "wb");
683 if (!*out)
685 strerror_r(errno, buf, buf_len);
686 QMessageBox::warning(
687 this,
688 "TTFautohint",
689 tr("The following error occurred while opening output file %1:\n")
690 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
691 + QString::fromLocal8Bit(buf),
692 QMessageBox::Ok,
693 QMessageBox::Ok);
694 return 0;
697 if (!control_name.isEmpty())
699 *control = fopen(qPrintable(control_name), "r");
700 if (!*control)
702 strerror_r(errno, buf, buf_len);
703 QMessageBox::warning(
704 this,
705 "TTFautohint",
706 tr("The following error occurred"
707 " while opening control instructions file %1:\n")
708 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name)))
709 + QString::fromLocal8Bit(buf),
710 QMessageBox::Ok,
711 QMessageBox::Ok);
712 return 0;
716 return 1;
720 void
721 Main_GUI::check_watch()
723 if (!watch_box->isChecked())
724 timer->stop();
725 // the timer gets started in the `run' function
729 void
730 Main_GUI::watch_files()
732 if (fileinfo_input_file.exists()
733 && fileinfo_input_file.isReadable()
734 && fileinfo_control_file.exists()
735 && fileinfo_control_file.isReadable())
737 QDateTime modified_input = fileinfo_input_file.lastModified();
738 QDateTime modified_control = fileinfo_control_file.lastModified();
739 if (modified_input > datetime_input_file
740 || modified_control > datetime_control_file)
741 run(); // this function sets `datetime_XXX'
743 else
744 run(); // let this routine handle all errors
748 extern "C" {
750 struct GUI_Progress_Data
752 long last_sfnt;
753 bool begin;
754 QProgressDialog* dialog;
758 #undef TRDOMAIN
759 #define TRDOMAIN "GuiProgress"
762 gui_progress(long curr_idx,
763 long num_glyphs,
764 long curr_sfnt,
765 long num_sfnts,
766 void* user)
768 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
770 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
772 data->dialog->setLabelText(Tr("Auto-hinting subfont %1 of %2"
773 " with %3 glyphs...")
774 .arg(curr_sfnt + 1)
775 .arg(num_sfnts)
776 .arg(num_glyphs));
778 if (curr_sfnt + 1 == num_sfnts)
780 data->dialog->setAutoReset(true);
781 data->dialog->setAutoClose(true);
783 else
785 data->dialog->setAutoReset(false);
786 data->dialog->setAutoClose(false);
789 data->last_sfnt = curr_sfnt;
790 data->begin = true;
793 if (data->begin)
795 if (num_sfnts == 1)
796 data->dialog->setLabelText(Tr("Auto-hinting %1 glyphs...")
797 .arg(num_glyphs));
798 data->dialog->setMaximum(num_glyphs - 1);
800 data->begin = false;
803 data->dialog->setValue(curr_idx);
805 if (data->dialog->wasCanceled())
806 return 1;
808 return 0;
812 struct GUI_Error_Data
814 Main_GUI* gui;
815 QLocale* locale;
816 QString output_name;
817 QString control_name;
818 int* ignore_restrictions_p;
819 bool retry;
823 #undef TRDOMAIN
824 #define TRDOMAIN "GuiError"
826 void
827 gui_error(TA_Error error,
828 const char* error_string,
829 unsigned int errlinenum,
830 const char* errline,
831 const char* errpos,
832 void* user)
834 GUI_Error_Data* data = static_cast<GUI_Error_Data*>(user);
835 QLocale* locale = data->locale; // for QUOTE_STRING_LITERAL
837 if (!error)
838 return;
840 if (error == TA_Err_Canceled)
842 else if (error == TA_Err_Invalid_FreeType_Version)
843 QMessageBox::critical(
844 data->gui,
845 "TTFautohint",
846 Tr("FreeType version 2.4.5 or higher is needed.\n"
847 "Are you perhaps using a wrong FreeType DLL?"),
848 QMessageBox::Ok,
849 QMessageBox::Ok);
850 else if (error == TA_Err_Invalid_Font_Type)
851 QMessageBox::warning(
852 data->gui,
853 "TTFautohint",
854 Tr("This font is not a valid font"
855 " in SFNT format with TrueType outlines.\n"
856 "In particular, CFF outlines are not supported."),
857 QMessageBox::Ok,
858 QMessageBox::Ok);
859 else if (error == TA_Err_Already_Processed)
860 QMessageBox::warning(
861 data->gui,
862 "TTFautohint",
863 Tr("This font has already been processed by TTFautohint."),
864 QMessageBox::Ok,
865 QMessageBox::Ok);
866 else if (error == TA_Err_Missing_Legal_Permission)
868 int yesno = QMessageBox::warning(
869 data->gui,
870 "TTFautohint",
871 Tr("Bit 1 in the %1 field of the %2 table is set:"
872 " This font must not be modified"
873 " without permission of the legal owner.\n"
874 "Do you have such a permission?")
875 .arg(QUOTE_STRING_LITERAL("fsType"))
876 .arg(QUOTE_STRING_LITERAL("OS/2")),
877 QMessageBox::Yes | QMessageBox::No,
878 QMessageBox::No);
879 if (yesno == QMessageBox::Yes)
881 *data->ignore_restrictions_p = true;
882 data->retry = true;
885 else if (error == TA_Err_Missing_Unicode_CMap)
886 QMessageBox::warning(
887 data->gui,
888 "TTFautohint",
889 Tr("The input font doesn't contain a Unicode character map.\n"
890 "Maybe you haven't set the %1 checkbox?")
891 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
892 QMessageBox::Ok,
893 QMessageBox::Ok);
894 else if (error == TA_Err_Missing_Symbol_CMap)
895 QMessageBox::warning(
896 data->gui,
897 "TTFautohint",
898 Tr("The input font does neither contain a symbol"
899 " nor a character map."),
900 QMessageBox::Ok,
901 QMessageBox::Ok);
902 else if (error == TA_Err_Missing_Glyph)
903 QMessageBox::warning(
904 data->gui,
905 "TTFautohint",
906 Tr("No glyph for a standard character"
907 " to derive standard width and height.\n"
908 "Please check the documentation for a list of"
909 " script-specific standard characters.\n"
910 "\n"
911 "Set the %1 checkbox if you want to circumvent this test.")
912 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
913 QMessageBox::Ok,
914 QMessageBox::Ok);
915 else if (error >= 0x200 && error < 0x300)
916 QMessageBox::warning(
917 data->gui,
918 "TTFautohint",
919 QString::fromLocal8Bit("%1:%2:%3: %4 (0x%5)<br>"
920 "<tt> %6<br>"
921 " %7</tt>")
922 .arg(data->control_name)
923 .arg(errlinenum)
924 .arg(int(errpos - errline + 1))
925 .arg(error_string)
926 .arg(error, 2, 16, QLatin1Char('0'))
927 .arg(errline)
928 .arg('^', int(errpos - errline + 1))
929 .replace(" ", "&nbsp;"),
930 QMessageBox::Ok,
931 QMessageBox::Ok);
932 else
933 QMessageBox::warning(
934 data->gui,
935 "TTFautohint",
936 Tr("Error code 0x%1 while autohinting font:\n")
937 .arg(error, 2, 16, QLatin1Char('0'))
938 + QString::fromLocal8Bit(error_string),
939 QMessageBox::Ok,
940 QMessageBox::Ok);
942 if (QFile::exists(data->output_name)
943 && remove(qPrintable(data->output_name)))
945 const int buf_len = 1024;
946 char buf[buf_len];
948 strerror_r(errno, buf, buf_len);
949 QMessageBox::warning(
950 data->gui,
951 "TTFautohint",
952 Tr("The following error occurred while removing output file %1:\n")
953 .arg(QUOTE_STRING(QDir::toNativeSeparators(data->output_name)))
954 + QString::fromLocal8Bit(buf),
955 QMessageBox::Ok,
956 QMessageBox::Ok);
960 } // extern "C"
963 void
964 Main_GUI::run()
966 statusBar()->clearMessage();
968 QString input_name = QDir::fromNativeSeparators(input_line->text());
969 QString output_name = QDir::fromNativeSeparators(output_line->text());
970 QString control_name = QDir::fromNativeSeparators(control_line->text());
971 if (!check_filenames(input_name, output_name, control_name))
973 timer->stop();
974 return;
977 // we need C file descriptors for communication with TTF_autohint
978 FILE* input;
979 FILE* output;
980 FILE* control;
982 again:
983 if (!open_files(input_name, &input,
984 output_name, &output,
985 control_name, &control))
987 timer->stop();
988 return;
991 QProgressDialog dialog;
992 dialog.setCancelButtonText(tr("Cancel"));
993 dialog.setMinimumDuration(1000);
994 dialog.setWindowModality(Qt::WindowModal);
996 TA_Info_Func info_func = info;
997 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
998 GUI_Error_Data gui_error_data = {this, locale, output_name, control_name,
999 &ignore_restrictions, false};
1001 fileinfo_input_file.setFile(input_name);
1002 fileinfo_control_file.setFile(control_name);
1004 Info_Data info_data;
1006 info_data.data = NULL; // must be deallocated after use
1007 info_data.data_wide = NULL; // must be deallocated after use
1008 info_data.data_len = 0;
1009 info_data.data_wide_len = 0;
1011 info_data.control_name = qPrintable(fileinfo_control_file.fileName());
1013 info_data.hinting_range_min = min_box->value();
1014 info_data.hinting_range_max = max_box->value();
1015 info_data.hinting_limit = no_limit_box->isChecked()
1017 : limit_box->value();
1019 info_data.gray_strong_stem_width = gray_box->isChecked();
1020 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
1021 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
1023 info_data.increase_x_height = no_increase_box->isChecked()
1025 : increase_box->value();
1026 info_data.x_height_snapping_exceptions_string =
1027 qPrintable(x_height_snapping_exceptions_string);
1028 info_data.fallback_stem_width = default_stem_width_box->isChecked()
1030 : stem_width_box->value();
1032 info_data.windows_compatibility = wincomp_box->isChecked();
1033 info_data.adjust_subglyphs = adjust_box->isChecked();
1034 info_data.hint_composites = hint_box->isChecked();
1035 info_data.symbol = symbol_box->isChecked();
1036 info_data.dehint = dehint_box->isChecked();
1037 info_data.TTFA_info = TTFA_box->isChecked();
1039 strncpy(info_data.default_script,
1040 script_names[default_box->currentIndex()].tag,
1041 sizeof (info_data.default_script));
1042 strncpy(info_data.fallback_script,
1043 script_names[fallback_box->currentIndex()].tag,
1044 sizeof (info_data.fallback_script));
1046 if (info_box->isChecked())
1048 int ret = build_version_string(&info_data);
1049 if (ret == 1)
1050 QMessageBox::information(
1051 this,
1052 "TTFautohint",
1053 tr("Can't allocate memory for <b>TTFautohint</b> options string"
1054 " in <i>name</i> table."),
1055 QMessageBox::Ok,
1056 QMessageBox::Ok);
1057 else if (ret == 2)
1058 QMessageBox::information(
1059 this,
1060 "TTFautohint",
1061 tr("<b>TTFautohint</b> options string"
1062 " in <i>name</i> table too long."),
1063 QMessageBox::Ok,
1064 QMessageBox::Ok);
1066 else
1067 info_func = NULL;
1069 if (info_data.symbol
1070 && info_data.fallback_stem_width
1071 && !strcmp(info_data.fallback_script, "none"))
1072 QMessageBox::information(
1073 this,
1074 "TTFautohint",
1075 tr("Setting a fallback stem width for a symbol font"
1076 " without setting a fallback script has no effect."),
1077 QMessageBox::Ok,
1078 QMessageBox::Ok);
1080 datetime_input_file = fileinfo_input_file.lastModified();
1081 datetime_control_file = fileinfo_control_file.lastModified();
1083 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
1085 TA_Error error =
1086 TTF_autohint("in-file, out-file, control-file,"
1087 "hinting-range-min, hinting-range-max,"
1088 "hinting-limit,"
1089 "gray-strong-stem-width,"
1090 "gdi-cleartype-strong-stem-width,"
1091 "dw-cleartype-strong-stem-width,"
1092 "progress-callback, progress-callback-data,"
1093 "error-callback, error-callback-data,"
1094 "info-callback, info-callback-data,"
1095 "ignore-restrictions,"
1096 "windows-compatibility,"
1097 "adjust-subglyphs,"
1098 "hint-composites,"
1099 "increase-x-height,"
1100 "x-height-snapping-exceptions, fallback-stem-width,"
1101 "default-script, fallback-script,"
1102 "symbol, dehint, TTFA-info",
1103 input, output, control,
1104 info_data.hinting_range_min, info_data.hinting_range_max,
1105 info_data.hinting_limit,
1106 info_data.gray_strong_stem_width,
1107 info_data.gdi_cleartype_strong_stem_width,
1108 info_data.dw_cleartype_strong_stem_width,
1109 gui_progress, &gui_progress_data,
1110 gui_error, &gui_error_data,
1111 info_func, &info_data,
1112 ignore_restrictions,
1113 info_data.windows_compatibility,
1114 info_data.adjust_subglyphs,
1115 info_data.hint_composites,
1116 info_data.increase_x_height,
1117 snapping_string.constData(), info_data.fallback_stem_width,
1118 info_data.default_script, info_data.fallback_script,
1119 info_data.symbol, info_data.dehint, info_data.TTFA_info);
1121 if (info_box->isChecked())
1123 free(info_data.data);
1124 free(info_data.data_wide);
1127 fclose(input);
1128 fclose(output);
1129 if (control)
1130 fclose(control);
1132 if (error)
1134 // retry if there is a user request to do so (handled in `gui_error')
1135 if (gui_error_data.retry)
1136 goto again;
1138 timer->stop();
1140 else
1142 statusBar()->showMessage(tr("Auto-hinting finished")
1143 + " ("
1144 + QDateTime::currentDateTime()
1145 .toString(Qt::TextDate)
1146 + ").");
1148 // we have successfully processed a file;
1149 // start file watching now if requested
1150 if (watch_box->isChecked())
1151 timer->start();
1156 // XXX distances are specified in pixels,
1157 // making the layout dependent on the output device resolution
1158 void
1159 Main_GUI::create_layout(bool horizontal_layout)
1162 // file stuff
1164 QCompleter* completer = new QCompleter(this);
1165 QFileSystemModel* model = new QFileSystemModel(completer);
1166 model->setRootPath(QDir::rootPath());
1167 completer->setModel(model);
1169 input_label = new QLabel(tr("&Input File:"));
1170 input_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1171 input_button = new QPushButton(tr("Browse..."));
1172 input_label->setBuddy(input_line);
1173 // enforce rich text to get nice word wrapping
1174 input_label->setToolTip(
1175 tr("<b></b>The input file, either a TrueType font (TTF),"
1176 " TrueType collection (TTC), or a TrueType-based OpenType font."));
1177 input_line->setCompleter(completer);
1179 output_label = new QLabel(tr("&Output File:"));
1180 output_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1181 output_button = new QPushButton(tr("Browse..."));
1182 output_label->setBuddy(output_line);
1183 output_label->setToolTip(
1184 tr("<b></b>The output file, which will be essentially identical"
1185 " to the input font but will contain new, generated hints."));
1186 output_line->setCompleter(completer);
1188 control_label = new QLabel(tr("Control Instructions Fil&e:"));
1189 control_line = new Drag_Drop_Line_Edit(DRAG_DROP_ANY);
1190 control_button = new QPushButton(tr("Browse..."));
1191 control_label->setBuddy(control_line);
1192 control_label->setToolTip(
1193 tr("<p>An optional control instructions file to tweak hinting."
1194 " This text file contains entries"
1195 " of one of the following syntax forms<br>"
1196 "&nbsp;<br>"
1197 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1198 "&nbsp;&nbsp;<i>glyph-id</i>"
1199 "&nbsp;&nbsp;<tt>l</tt>|<tt>r</tt>|<tt>n</tt>&nbsp;<i>points</i><br>"
1200 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1201 "&nbsp;&nbsp;<i>glyph-id</i>"
1202 "&nbsp;&nbsp;<tt>p</tt>&nbsp;<i>points</i>"
1203 "&nbsp;&nbsp;[&nbsp;<tt>x</tt>&nbsp;<i>shift</i>&nbsp;]"
1204 "&nbsp;&nbsp;[&nbsp;<tt>y</tt>&nbsp;<i>shift</i>&nbsp;]"
1205 "&nbsp;&nbsp;<tt>@</tt>&nbsp;<i>ppems</i><br>"
1206 "&nbsp;<br>"
1207 "<i>subfont-idx</i> gives the subfont index in a TTC,"
1208 " <i>glyph-id</i> is a glyph name or index.<br>"
1209 "<tt>l</tt> (<tt>r</tt>, <tt>n</tt>) force point directions"
1210 " to be left (right, no direction).<br>"
1211 "<tt>p</tt> defines delta exceptions"
1212 " (to be applied <i>after</i> the final"
1213 " <tt>IP</tt> bytecode instructions;"
1214 " this usually fails in ClearType mode).<br>"
1215 "x and y <i>shift</i> values are in the range [-1;1],"
1216 " rounded to multiples of 1/8px.<br>"
1217 "<i>points</i> and <i>ppems</i> are ranges for point indices"
1218 " and ppem values as with x&nbsp;height snapping exceptions.<br>"
1219 "Control instruction entries are separated"
1220 " by character&nbsp;<tt>;</tt> or by a newline.<br>"
1221 " A trailing character&nbsp;<tt>\\</tt> continues the current line"
1222 " on the next line.<br>"
1223 "<tt>#</tt> starts a line comment, which gets ignored."
1224 " Empty lines are ignored, too.</p>"
1226 "Examples:<br>"
1227 "&nbsp;&nbsp;<tt>Q l 38</tt><br>"
1228 "&nbsp;&nbsp;<tt>Adieresis p 3-6 y 0.25 @ 13</tt>"));
1229 control_line->setCompleter(completer);
1232 // minmax controls
1234 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
1235 min_box = new QSpinBox;
1236 min_label->setBuddy(min_box);
1237 min_label->setToolTip(
1238 tr("The minimum PPEM value of the range for which"
1239 " <b>TTFautohint</b> computes <i>hint sets</i>."
1240 " A hint set for a given PPEM value hints this size optimally."
1241 " The larger the range, the more hint sets are considered,"
1242 " usually increasing the size of the bytecode.<br>"
1243 "Note that changing this range doesn't influence"
1244 " the <i>gasp</i> table:"
1245 " Hinting is enabled for all sizes."));
1246 min_box->setKeyboardTracking(false);
1247 min_box->setRange(2, 10000);
1249 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
1250 max_box = new QSpinBox;
1251 max_label->setBuddy(max_box);
1252 max_label->setToolTip(
1253 tr("The maximum PPEM value of the range for which"
1254 " <b>TTFautohint</b> computes <i>hint sets</i>."
1255 " A hint set for a given PPEM value hints this size optimally."
1256 " The larger the range, the more hint sets are considered,"
1257 " usually increasing the size of the bytecode.<br>"
1258 "Note that changing this range doesn't influence"
1259 " the <i>gasp</i> table:"
1260 " Hinting is enabled for all sizes."));
1261 max_box->setKeyboardTracking(false);
1262 max_box->setRange(2, 10000);
1265 // OpenType default script
1267 default_label = new QLabel(tr("Defa&ult Script:"));
1268 default_box = new QComboBox;
1269 default_label->setBuddy(default_box);
1270 default_label->setToolTip(
1271 tr("This sets the default script for OpenType features:"
1272 " After applying all features that are handled specially"
1273 " (for example small caps or superscript glyphs),"
1274 " <b>TTFautohint</b> uses this value for the remaining features."));
1275 for (int i = 0; script_names[i].tag; i++)
1277 // XXX: how to provide translations?
1278 default_box->insertItem(i,
1279 QString("%1 (%2)")
1280 .arg(script_names[i].tag)
1281 .arg(script_names[i].description));
1285 // hinting and fallback controls
1287 fallback_label = new QLabel(tr("Fallback &Script:"));
1288 fallback_box = new QComboBox;
1289 fallback_label->setBuddy(fallback_box);
1290 fallback_label->setToolTip(
1291 tr("This sets the fallback script for glyphs"
1292 " that <b>TTFautohint</b> can't map to a script automatically."));
1293 for (int i = 0; script_names[i].tag; i++)
1295 // XXX: how to provide translations?
1296 fallback_box->insertItem(i,
1297 QString("%1 (%2)")
1298 .arg(script_names[i].tag)
1299 .arg(script_names[i].description));
1303 // hinting limit
1305 limit_label_text_with_key = tr("Hinting &Limit:");
1306 limit_label_text = tr("Hinting Limit:");
1307 limit_label = new QLabel(limit_label_text_with_key);
1308 limit_box = new QSpinBox;
1309 limit_label->setBuddy(limit_box);
1310 limit_label->setToolTip(
1311 tr("Switch off hinting for PPEM values exceeding this limit."
1312 " Changing this value does not influence the size of the bytecode.<br>"
1313 "Note that <b>TTFautohint</b> handles this feature"
1314 " in the output font's bytecode and not in the <i>gasp</i> table."));
1315 limit_box->setKeyboardTracking(false);
1316 limit_box->setRange(2, 10000);
1318 no_limit_box_text_with_key = tr("No Hinting &Limit");
1319 no_limit_box_text = tr("No Hinting Limit");
1320 no_limit_box = new QCheckBox(no_limit_box_text, this);
1321 no_limit_box->setToolTip(
1322 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
1323 " to the bytecode.<br>"
1324 "For testing only."));
1327 // x height increase limit
1329 increase_label_text_with_key = tr("x Height In&crease Limit:");
1330 increase_label_text = tr("x Height Increase Limit:");
1331 increase_label = new QLabel(increase_label_text_with_key);
1332 increase_box = new QSpinBox;
1333 increase_label->setBuddy(increase_box);
1334 increase_label->setToolTip(
1335 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
1336 " where <i>n</i> is the value selected by this spin box,"
1337 " round up the font's x&nbsp;height much more often than normally.<br>"
1338 "Use this if holes in letters like <i>e</i> get filled,"
1339 " for example."));
1340 increase_box->setKeyboardTracking(false);
1341 increase_box->setRange(6, 10000);
1343 no_increase_box_text_with_key = tr("No x Height In&crease");
1344 no_increase_box_text = tr("No x Height Increase");
1345 no_increase_box = new QCheckBox(no_increase_box_text, this);
1346 no_increase_box->setToolTip(
1347 tr("If switched on,"
1348 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
1351 // x height snapping exceptions
1353 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
1354 snapping_line = new Tooltip_Line_Edit;
1355 snapping_label->setBuddy(snapping_line);
1356 snapping_label->setToolTip(
1357 tr("<p>A list of comma separated PPEM values or value ranges"
1358 " at which no x&nbsp;height snapping shall be applied"
1359 " (x&nbsp;height snapping usually slightly increases"
1360 " the size of all glyphs).</p>"
1362 "Examples:<br>"
1363 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1364 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1365 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1366 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1369 // fallback stem width
1371 stem_width_label_text_with_key = tr("Fallbac&k Stem Width:");
1372 stem_width_label_text = tr("Fallback Stem Width:");
1373 stem_width_label = new QLabel(stem_width_label_text_with_key);
1374 stem_width_box = new QSpinBox;
1375 stem_width_label->setBuddy(stem_width_box);
1376 stem_width_label->setToolTip(
1377 tr("Set horizontal stem width (in font units) for all scripts"
1378 " that lack proper standard characters in the font.<br>"
1379 "If not set, <b>TTFautohint</b> uses a hard-coded default value."));
1380 stem_width_box->setKeyboardTracking(false);
1381 stem_width_box->setRange(1, 10000);
1383 default_stem_width_box_text_with_key = tr("Default Fallbac&k Stem Width");
1384 default_stem_width_box_text = tr("Default Fallback Stem Width");
1385 default_stem_width_box = new QCheckBox(default_stem_width_box_text, this);
1386 default_stem_width_box->setToolTip(
1387 tr("If switched on, <b>TTFautohint</b> uses a default value"
1388 " for the fallback stem width (50 font units at 2048 UPEM)."));
1391 // flags
1393 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1394 wincomp_box->setToolTip(
1395 tr("If switched on, add two artificial blue zones positioned at the"
1396 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1397 " (from the font's <i>OS/2</i> table)."
1398 " This option, usually in combination"
1399 " with value <tt>-</tt> (a single dash)"
1400 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1401 " should be used if those two <i>OS/2</i> values are tight,"
1402 " and you are experiencing clipping during rendering."));
1404 adjust_box = new QCheckBox(tr("Ad&just Subglyphs"), this);
1405 adjust_box->setToolTip(
1406 tr("If switched on, the original bytecode of the input font"
1407 " gets applied (at EM size, usually 2048ppem)"
1408 " to derive the glyph outlines for <b>TTFautohint</b>.<br>"
1409 "Use this option only if subglyphs"
1410 " are incorrectly scaled and shifted.<br>"
1411 "Note that the original bytecode will always be discarded."));
1413 hint_box = new QCheckBox(tr("Hint Co&mposites")
1414 + " ", this); // make label wider
1415 hint_box->setToolTip(
1416 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1417 " as a whole, including subglyphs."
1418 " Otherwise, glyph components get hinted separately.<br>"
1419 "Deactivating this flag reduces the bytecode size enormously,"
1420 " however, it might yield worse results."));
1422 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1423 symbol_box->setToolTip(
1424 tr("If switched on, <b>TTFautohint</b> accepts fonts"
1425 " that don't contain a single standard character"
1426 " for any of the supported scripts.<br>"
1427 "Use this for symbol or dingbat fonts, for example."));
1429 dehint_box = new QCheckBox(tr("&Dehint"), this);
1430 dehint_box->setToolTip(
1431 tr("If set, remove all hints from the font.<br>"
1432 "For testing only."));
1434 info_box = new QCheckBox(tr("Add ttf&autohint Info"), this);
1435 info_box->setToolTip(
1436 tr("If switched on, information about <b>TTFautohint</b>"
1437 " and its calling parameters are added to the version string(s)"
1438 " (name ID&nbsp;5) in the <i>name</i> table."));
1440 TTFA_box = new QCheckBox(tr("Add TTFA Info Ta&ble"), this);
1441 TTFA_box->setToolTip(
1442 tr("If switched on, an SFNT table called <tt>TTFA</tt>"
1443 " gets added to the output font,"
1444 " holding a dump of all parameters."
1445 " In particular, it lists all control instructions."));
1448 // stem width and positioning
1450 stem_label = new QLabel(tr("Stron&g Stem Width and Positioning:"));
1451 stem_label->setToolTip(
1452 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1453 " that can be selected for various hinting modes."
1455 "<p><i>strong</i> (checkbox set):"
1456 " Position horizontal stems and snap stem widths"
1457 " to integer pixel values. While making the output look crisper,"
1458 " outlines become more distorted.</p>"
1460 "<p><i>smooth</i> (checkbox not set):"
1461 " Use discrete values for horizontal stems and stem widths."
1462 " This only slightly increases the contrast"
1463 " but avoids larger outline distortion.</p>"));
1465 gray_box = new QCheckBox(tr("Grayscale"), this);
1466 gray_box->setToolTip(
1467 tr("<b></b>Grayscale rendering, no ClearType activated."));
1468 stem_label->setBuddy(gray_box);
1470 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1471 gdi_box->setToolTip(
1472 tr("GDI ClearType rendering,"
1473 " introduced in 2000 for Windows XP.<br>"
1474 "The rasterizer version (as returned by the"
1475 " GETINFO bytecode instruction) is in the range"
1476 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1477 "Along the vertical axis, this mode behaves like B/W rendering."));
1479 dw_box = new QCheckBox(tr("DW ClearType"), this);
1480 dw_box->setToolTip(
1481 tr("DirectWrite ClearType rendering,"
1482 " introduced in 2008 for Windows Vista.<br>"
1483 "The rasterizer version (as returned by the"
1484 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1485 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1486 "Smooth rendering along the vertical axis."));
1489 // running
1491 watch_box = new QCheckBox(tr("&Watch Input Files"), this);
1492 watch_box->setToolTip(
1493 tr("If switched on, <b>TTFautohint</b> automatically re-runs"
1494 " the hinting process as soon as an input file"
1495 " (either the font or the control instructions file) is modified.<br>"
1496 "Pressing the %1 button starts watching.<br>"
1497 "If an error occurs, watching stops and must be restarted"
1498 " with the %1 button.")
1499 .arg(QUOTE_STRING_LITERAL(tr("Run"))));
1501 run_button = new QPushButton(" "
1502 + tr("&Run")
1503 + " "); // make label wider
1505 if (horizontal_layout)
1506 create_horizontal_layout();
1507 else
1508 create_vertical_layout();
1512 // XXX distances are specified in pixels,
1513 // making the layout dependent on the output device resolution
1514 void
1515 Main_GUI::create_vertical_layout()
1517 // top area
1518 QGridLayout* file_layout = new QGridLayout;
1520 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1521 file_layout->addWidget(input_line, 0, 1);
1522 file_layout->addWidget(input_button, 0, 2);
1524 file_layout->setRowStretch(1, 1);
1526 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1527 file_layout->addWidget(output_line, 2, 1);
1528 file_layout->addWidget(output_button, 2, 2);
1530 file_layout->setRowStretch(3, 1);
1532 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1533 file_layout->addWidget(control_line, 4, 1);
1534 file_layout->addWidget(control_button, 4, 2);
1536 // bottom area
1537 QGridLayout* run_layout = new QGridLayout;
1539 run_layout->addWidget(watch_box, 0, 1);
1540 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1541 run_layout->setColumnStretch(0, 1);
1542 run_layout->setColumnStretch(2, 2);
1545 // the whole gui
1547 QGridLayout* gui_layout = new QGridLayout;
1548 QFrame* hline = new QFrame;
1549 hline->setFrameShape(QFrame::HLine);
1550 int row = 0; // this counter simplifies inserting new items
1552 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1553 gui_layout->setRowStretch(row++, 1);
1555 gui_layout->addLayout(file_layout, row, 0, row, -1);
1556 gui_layout->setRowStretch(row++, 1);
1558 gui_layout->addWidget(hline, row, 0, row, -1);
1559 gui_layout->setRowStretch(row++, 1);
1561 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1562 gui_layout->setRowStretch(row++, 1);
1564 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1565 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1566 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1567 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1569 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1570 gui_layout->setRowStretch(row++, 1);
1572 gui_layout->addWidget(default_label, row, 0, Qt::AlignRight);
1573 gui_layout->addWidget(default_box, row++, 1, Qt::AlignLeft);
1574 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1575 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1577 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1578 gui_layout->setRowStretch(row++, 1);
1580 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1581 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1582 gui_layout->addWidget(no_limit_box, row++, 1);
1584 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1585 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1586 gui_layout->addWidget(no_increase_box, row++, 1);
1588 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1589 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1591 gui_layout->addWidget(stem_width_label, row, 0, Qt::AlignRight);
1592 gui_layout->addWidget(stem_width_box, row++, 1, Qt::AlignLeft);
1593 gui_layout->addWidget(default_stem_width_box, row++, 1);
1595 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1596 gui_layout->setRowStretch(row++, 1);
1598 gui_layout->addWidget(wincomp_box, row++, 1);
1599 gui_layout->addWidget(adjust_box, row++, 1);
1600 gui_layout->addWidget(hint_box, row++, 1);
1601 gui_layout->addWidget(symbol_box, row++, 1);
1602 gui_layout->addWidget(dehint_box, row++, 1);
1603 gui_layout->addWidget(info_box, row++, 1);
1604 gui_layout->addWidget(TTFA_box, row++, 1);
1606 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1607 gui_layout->setRowStretch(row++, 1);
1609 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1610 gui_layout->addWidget(gray_box, row++, 1);
1611 gui_layout->addWidget(gdi_box, row++, 1);
1612 gui_layout->addWidget(dw_box, row++, 1);
1614 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1615 gui_layout->setRowStretch(row++, 1);
1617 gui_layout->addLayout(run_layout, row, 0, row, -1);
1619 // create dummy widget to register layout
1620 QWidget* main_widget = new QWidget;
1621 main_widget->setLayout(gui_layout);
1622 setCentralWidget(main_widget);
1623 setWindowTitle("TTFautohint");
1627 // XXX distances are specified in pixels,
1628 // making the layout dependent on the output device resolution
1629 void
1630 Main_GUI::create_horizontal_layout()
1632 // top area
1633 QGridLayout* file_layout = new QGridLayout;
1635 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1636 file_layout->addWidget(input_line, 0, 1);
1637 file_layout->addWidget(input_button, 0, 2);
1639 file_layout->setRowStretch(1, 1);
1641 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1642 file_layout->addWidget(output_line, 2, 1);
1643 file_layout->addWidget(output_button, 2, 2);
1645 file_layout->setRowStretch(3, 1);
1647 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1648 file_layout->addWidget(control_line, 4, 1);
1649 file_layout->addWidget(control_button, 4, 2);
1651 // bottom area
1652 QGridLayout* run_layout = new QGridLayout;
1654 run_layout->addWidget(watch_box, 0, 1);
1655 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1656 run_layout->setColumnStretch(0, 2);
1657 run_layout->setColumnStretch(2, 3);
1658 run_layout->setColumnStretch(4, 1);
1661 // the whole gui
1663 QGridLayout* gui_layout = new QGridLayout;
1664 QFrame* hline = new QFrame;
1665 hline->setFrameShape(QFrame::HLine);
1666 int row = 0; // this counter simplifies inserting new items
1668 // margin
1669 gui_layout->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1670 gui_layout->setColumnStretch(0, 1);
1672 // left
1673 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1674 gui_layout->setRowStretch(row++, 1);
1676 gui_layout->addLayout(file_layout, row, 0, row, -1);
1677 gui_layout->setRowStretch(row++, 1);
1679 gui_layout->addWidget(hline, row, 0, row, -1);
1680 gui_layout->setRowStretch(row++, 1);
1682 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1683 gui_layout->setRowStretch(row++, 1);
1685 gui_layout->addWidget(min_label, row, 1, Qt::AlignRight);
1686 gui_layout->addWidget(min_box, row++, 2, Qt::AlignLeft);
1687 gui_layout->addWidget(max_label, row, 1, Qt::AlignRight);
1688 gui_layout->addWidget(max_box, row++, 2, Qt::AlignLeft);
1690 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1691 gui_layout->setRowStretch(row++, 1);
1693 gui_layout->addWidget(default_label, row, 1, Qt::AlignRight);
1694 gui_layout->addWidget(default_box, row++, 2, Qt::AlignLeft);
1695 gui_layout->addWidget(fallback_label, row, 1, Qt::AlignRight);
1696 gui_layout->addWidget(fallback_box, row++, 2, Qt::AlignLeft);
1698 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1699 gui_layout->setRowStretch(row++, 1);
1701 gui_layout->addWidget(limit_label, row, 1, Qt::AlignRight);
1702 gui_layout->addWidget(limit_box, row++, 2, Qt::AlignLeft);
1703 gui_layout->addWidget(no_limit_box, row++, 2);
1705 gui_layout->addWidget(increase_label, row, 1, Qt::AlignRight);
1706 gui_layout->addWidget(increase_box, row++, 2, Qt::AlignLeft);
1707 gui_layout->addWidget(no_increase_box, row++, 2);
1709 gui_layout->addWidget(snapping_label, row, 1, Qt::AlignRight);
1710 gui_layout->addWidget(snapping_line, row++, 2, Qt::AlignLeft);
1712 gui_layout->addWidget(stem_width_label, row, 1, Qt::AlignRight);
1713 gui_layout->addWidget(stem_width_box, row++, 2, Qt::AlignLeft);
1714 gui_layout->addWidget(default_stem_width_box, row++, 2);
1716 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1717 gui_layout->setRowStretch(row++, 1);
1719 gui_layout->addLayout(run_layout, row, 0, row, -1);
1721 // column separator
1722 gui_layout->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1723 gui_layout->setColumnStretch(3, 1);
1725 // right
1726 row = 4;
1727 gui_layout->addWidget(wincomp_box, row++, 4);
1728 gui_layout->addWidget(adjust_box, row++, 4);
1729 gui_layout->addWidget(hint_box, row++, 4);
1730 gui_layout->addWidget(symbol_box, row++, 4);
1731 gui_layout->addWidget(dehint_box, row++, 4);
1732 gui_layout->addWidget(info_box, row++, 4);
1733 gui_layout->addWidget(TTFA_box, row++, 4);
1735 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1736 gui_layout->setRowStretch(row++, 1);
1738 gui_layout->addWidget(stem_label, row++, 4);
1740 QGridLayout* stem_layout = new QGridLayout;
1741 stem_layout->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1742 stem_layout->addWidget(gray_box, 0, 1);
1743 stem_layout->addWidget(gdi_box, 1, 1);
1744 stem_layout->addWidget(dw_box, 2, 1);
1746 gui_layout->addLayout(stem_layout, row, 4, 3, 1);
1747 row += 3;
1749 // margin
1750 gui_layout->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1751 gui_layout->setColumnStretch(5, 1);
1753 // create dummy widget to register layout
1754 QWidget* main_widget = new QWidget;
1755 main_widget->setLayout(gui_layout);
1756 setCentralWidget(main_widget);
1757 setWindowTitle("TTFautohint");
1761 void
1762 Main_GUI::create_connections()
1764 connect(input_button, SIGNAL(clicked()), this,
1765 SLOT(browse_input()));
1766 connect(output_button, SIGNAL(clicked()), this,
1767 SLOT(browse_output()));
1768 connect(control_button, SIGNAL(clicked()), this,
1769 SLOT(browse_control()));
1771 connect(input_line, SIGNAL(textChanged(QString)), this,
1772 SLOT(check_run()));
1773 connect(output_line, SIGNAL(textChanged(QString)), this,
1774 SLOT(check_run()));
1776 connect(input_line, SIGNAL(editingFinished()), this,
1777 SLOT(absolute_input()));
1778 connect(output_line, SIGNAL(editingFinished()), this,
1779 SLOT(absolute_output()));
1780 connect(control_line, SIGNAL(editingFinished()), this,
1781 SLOT(absolute_control()));
1783 connect(min_box, SIGNAL(valueChanged(int)), this,
1784 SLOT(check_min()));
1785 connect(max_box, SIGNAL(valueChanged(int)), this,
1786 SLOT(check_max()));
1788 connect(limit_box, SIGNAL(valueChanged(int)), this,
1789 SLOT(check_limit()));
1790 connect(no_limit_box, SIGNAL(clicked()), this,
1791 SLOT(check_no_limit()));
1793 connect(no_increase_box, SIGNAL(clicked()), this,
1794 SLOT(check_no_increase()));
1796 connect(snapping_line, SIGNAL(editingFinished()), this,
1797 SLOT(check_number_set()));
1798 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1799 SLOT(clear_status_bar()));
1801 connect(default_stem_width_box, SIGNAL(clicked()), this,
1802 SLOT(check_default_stem_width()));
1804 connect(dehint_box, SIGNAL(clicked()), this,
1805 SLOT(check_dehint()));
1807 connect(timer, SIGNAL(timeout()), this,
1808 SLOT(watch_files()));
1810 connect(watch_box, SIGNAL(clicked()), this,
1811 SLOT(check_watch()));
1813 connect(run_button, SIGNAL(clicked()), this,
1814 SLOT(run()));
1818 void
1819 Main_GUI::create_actions()
1821 exit_act = new QAction(tr("E&xit"), this);
1822 exit_act->setShortcuts(QKeySequence::Quit);
1823 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1825 about_act = new QAction(tr("&About"), this);
1826 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1828 about_Qt_act = new QAction(tr("About &Qt"), this);
1829 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1833 void
1834 Main_GUI::create_menus()
1836 file_menu = menuBar()->addMenu(tr("&File"));
1837 file_menu->addAction(exit_act);
1839 help_menu = menuBar()->addMenu(tr("&Help"));
1840 help_menu->addAction(about_act);
1841 help_menu->addAction(about_Qt_act);
1845 void
1846 Main_GUI::create_status_bar()
1848 statusBar()->showMessage("");
1852 void
1853 Main_GUI::set_defaults()
1855 min_box->setValue(hinting_range_min);
1856 max_box->setValue(hinting_range_max);
1858 default_box->setCurrentIndex(default_script_idx);
1859 fallback_box->setCurrentIndex(fallback_script_idx);
1861 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1862 // handle command line option `--hinting-limit=0'
1863 if (!hinting_limit)
1865 hinting_limit = max_box->value();
1866 no_limit_box->setChecked(true);
1869 increase_box->setValue(increase_x_height ? increase_x_height
1870 : TA_INCREASE_X_HEIGHT);
1871 // handle command line option `--increase-x-height=0'
1872 if (!increase_x_height)
1874 increase_x_height = TA_INCREASE_X_HEIGHT;
1875 no_increase_box->setChecked(true);
1878 snapping_line->setText(x_height_snapping_exceptions_string);
1880 if (fallback_stem_width)
1881 stem_width_box->setValue(fallback_stem_width);
1882 else
1884 stem_width_box->setValue(50);
1885 default_stem_width_box->setChecked(true);
1888 if (windows_compatibility)
1889 wincomp_box->setChecked(true);
1890 if (adjust_subglyphs)
1891 adjust_box->setChecked(true);
1892 if (hint_composites)
1893 hint_box->setChecked(true);
1894 if (symbol)
1895 symbol_box->setChecked(true);
1896 if (dehint)
1897 dehint_box->setChecked(true);
1898 if (!no_info)
1899 info_box->setChecked(true);
1900 if (TTFA_info)
1901 info_box->setChecked(true);
1903 if (gray_strong_stem_width)
1904 gray_box->setChecked(true);
1905 if (gdi_cleartype_strong_stem_width)
1906 gdi_box->setChecked(true);
1907 if (dw_cleartype_strong_stem_width)
1908 dw_box->setChecked(true);
1910 run_button->setEnabled(false);
1912 check_min();
1913 check_max();
1914 check_limit();
1916 check_no_limit();
1917 check_no_increase();
1918 check_number_set();
1920 // do this last since it disables almost everything
1921 check_dehint();
1925 void
1926 Main_GUI::read_settings()
1928 QSettings settings;
1929 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1930 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1931 // resize(size);
1932 // move(pos);
1936 void
1937 Main_GUI::write_settings()
1939 QSettings settings;
1940 // settings.setValue("pos", pos());
1941 // settings.setValue("size", size());
1944 // end of maingui.cpp