Add `no_info' flag to `Info_Data'.
[ttfautohint.git] / frontend / maingui.cpp
blobf3235333e852fc2a57e2ddfc09a568cca5539a90
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 bool symb,
111 bool dh,
112 bool TTFA)
113 : hinting_range_min(range_min),
114 hinting_range_max(range_max),
115 hinting_limit(limit),
116 gray_strong_stem_width(gray),
117 gdi_cleartype_strong_stem_width(gdi),
118 dw_cleartype_strong_stem_width(dw),
119 increase_x_height(increase),
120 x_height_snapping_exceptions_string(exceptions),
121 fallback_stem_width(stem_width),
122 ignore_restrictions(ignore),
123 windows_compatibility(wincomp),
124 adjust_subglyphs(adjust),
125 hint_composites(composites),
126 no_info(no),
127 detailed_info(detailed),
128 symbol(symb),
129 dehint(dh),
130 TTFA_info(TTFA)
132 int i;
134 // map default script tag to an index,
135 // replacing an invalid one with the default value
136 int latn_script_idx = 0;
137 for (i = 0; script_names[i].tag; i++)
139 if (!strcmp("latn", script_names[i].tag))
140 latn_script_idx = i;
141 if (!strcmp(dflt, script_names[i].tag))
142 break;
144 default_script_idx = script_names[i].tag ? i : latn_script_idx;
146 // map fallback script tag to an index,
147 // replacing an invalid one with the default value
148 int none_script_idx = 0;
149 for (i = 0; script_names[i].tag; i++)
151 if (!strcmp("none", script_names[i].tag))
152 none_script_idx = i;
153 if (!strcmp(fallback, script_names[i].tag))
154 break;
156 fallback_script_idx = script_names[i].tag ? i : none_script_idx;
158 x_height_snapping_exceptions = NULL;
160 // if file watching is active, we regularly poll the file status
161 timer = new QTimer(this);
162 timer->setInterval(1000);
163 fileinfo_input_file.setCaching(false);
164 fileinfo_control_file.setCaching(false);
166 // XXX register translations somewhere and loop over them
167 if (QLocale::system().name() == "en_US")
168 locale = new QLocale;
169 else
170 locale = new QLocale(QLocale::C);
172 // For real numbers (both parsing and displaying) we only use `.' as the
173 // decimal separator; similarly, we don't want localized formats like a
174 // thousands separator for any number.
175 setlocale(LC_NUMERIC, "C");
177 create_layout(horizontal_layout);
178 create_connections();
179 create_actions();
180 create_menus();
181 create_status_bar();
183 set_defaults();
184 read_settings();
186 setUnifiedTitleAndToolBarOnMac(true);
190 Main_GUI::~Main_GUI()
192 number_set_free(x_height_snapping_exceptions);
196 // overloading
198 void
199 Main_GUI::closeEvent(QCloseEvent* event)
201 write_settings();
202 event->accept();
206 void
207 Main_GUI::about()
209 QMessageBox::about(this,
210 tr("About TTFautohint"),
211 tr("<p>This is <b>TTFautohint</b> version %1<br>"
212 " Copyright %2 2011-2014<br>"
213 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
215 "<p><b>TTFautohint</b> adds new auto-generated hints"
216 " to a TrueType font or TrueType collection.</p>"
218 "<p>License:"
219 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT'>FreeType"
220 " License (FTL)</a> or"
221 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT'>GNU"
222 " GPLv2</a></p>")
223 .arg(VERSION)
224 .arg(QChar(0xA9)));
228 void
229 Main_GUI::browse_input()
231 // XXX remember last directory
232 QString file = QFileDialog::getOpenFileName(
233 this,
234 tr("Open Input File"),
235 QDir::homePath(),
236 "");
237 if (!file.isEmpty())
238 input_line->setText(QDir::toNativeSeparators(file));
242 void
243 Main_GUI::browse_output()
245 // XXX remember last directory
246 QString file = QFileDialog::getSaveFileName(
247 this,
248 tr("Open Output File"),
249 QDir::homePath(),
250 "");
252 if (!file.isEmpty())
253 output_line->setText(QDir::toNativeSeparators(file));
257 void
258 Main_GUI::browse_control()
260 // XXX remember last directory
261 QString file = QFileDialog::getOpenFileName(
262 this,
263 tr("Open Control INstructions File"),
264 QDir::homePath(),
265 "");
267 if (!file.isEmpty())
268 control_line->setText(QDir::toNativeSeparators(file));
272 void
273 Main_GUI::check_min()
275 int min = min_box->value();
276 int max = max_box->value();
277 int limit = limit_box->value();
278 if (min > max)
279 max_box->setValue(min);
280 if (min > limit)
281 limit_box->setValue(min);
285 void
286 Main_GUI::check_max()
288 int min = min_box->value();
289 int max = max_box->value();
290 int limit = limit_box->value();
291 if (max < min)
292 min_box->setValue(max);
293 if (max > limit)
294 limit_box->setValue(max);
298 void
299 Main_GUI::check_limit()
301 int min = min_box->value();
302 int max = max_box->value();
303 int limit = limit_box->value();
304 if (limit < max)
305 max_box->setValue(limit);
306 if (limit < min)
307 min_box->setValue(limit);
311 void
312 Main_GUI::check_dehint()
314 if (dehint_box->isChecked())
316 min_label->setEnabled(false);
317 min_box->setEnabled(false);
319 max_label->setEnabled(false);
320 max_box->setEnabled(false);
322 default_label->setEnabled(false);
323 default_box->setEnabled(false);
324 fallback_label->setEnabled(false);
325 fallback_box->setEnabled(false);
327 no_limit_box->setEnabled(false);
328 limit_label->setEnabled(false);
329 limit_box->setEnabled(false);
331 no_increase_box->setEnabled(false);
332 increase_label->setEnabled(false);
333 increase_box->setEnabled(false);
335 snapping_label->setEnabled(false);
336 snapping_line->setEnabled(false);
338 default_stem_width_box->setEnabled(false);
339 stem_width_label->setEnabled(false);
340 stem_width_box->setEnabled(false);
342 wincomp_box->setEnabled(false);
343 adjust_box->setEnabled(false);
344 hint_box->setEnabled(false);
345 symbol_box->setEnabled(false);
347 stem_label->setEnabled(false);
348 gray_box->setEnabled(false);
349 gdi_box->setEnabled(false);
350 dw_box->setEnabled(false);
352 else
354 min_label->setEnabled(true);
355 min_box->setEnabled(true);
357 max_label->setEnabled(true);
358 max_box->setEnabled(true);
360 default_label->setEnabled(true);
361 default_box->setEnabled(true);
362 fallback_label->setEnabled(true);
363 fallback_box->setEnabled(true);
365 no_limit_box->setEnabled(true);
366 check_no_limit();
368 no_increase_box->setEnabled(true);
369 check_no_increase();
371 snapping_label->setEnabled(true);
372 snapping_line->setEnabled(true);
374 default_stem_width_box->setEnabled(true);
375 check_default_stem_width();
377 wincomp_box->setEnabled(true);
378 adjust_box->setEnabled(true);
379 hint_box->setEnabled(true);
380 symbol_box->setEnabled(true);
382 stem_label->setEnabled(true);
383 gray_box->setEnabled(true);
384 gdi_box->setEnabled(true);
385 dw_box->setEnabled(true);
390 void
391 Main_GUI::check_no_limit()
393 if (no_limit_box->isChecked())
395 limit_label->setEnabled(false);
396 limit_label->setText(limit_label_text);
397 limit_box->setEnabled(false);
398 no_limit_box->setText(no_limit_box_text_with_key);
400 else
402 limit_label->setEnabled(true);
403 limit_label->setText(limit_label_text_with_key);
404 limit_box->setEnabled(true);
405 no_limit_box->setText(no_limit_box_text);
410 void
411 Main_GUI::check_no_increase()
413 if (no_increase_box->isChecked())
415 increase_label->setEnabled(false);
416 increase_label->setText(increase_label_text);
417 increase_box->setEnabled(false);
418 no_increase_box->setText(no_increase_box_text_with_key);
420 else
422 increase_label->setEnabled(true);
423 increase_label->setText(increase_label_text_with_key);
424 increase_box->setEnabled(true);
425 no_increase_box->setText(no_increase_box_text);
430 void
431 Main_GUI::check_default_stem_width()
433 if (default_stem_width_box->isChecked())
435 stem_width_label->setEnabled(false);
436 stem_width_label->setText(stem_width_label_text);
437 stem_width_box->setEnabled(false);
438 default_stem_width_box->setText(default_stem_width_box_text_with_key);
440 else
442 stem_width_label->setEnabled(true);
443 stem_width_label->setText(stem_width_label_text_with_key);
444 stem_width_box->setEnabled(true);
445 default_stem_width_box->setText(default_stem_width_box_text);
450 void
451 Main_GUI::check_run()
453 if (input_line->text().isEmpty() || output_line->text().isEmpty())
454 run_button->setEnabled(false);
455 else
456 run_button->setEnabled(true);
460 void
461 Main_GUI::absolute_input()
463 QString input_name = QDir::fromNativeSeparators(input_line->text());
464 if (!input_name.isEmpty()
465 && QDir::isRelativePath(input_name))
467 QDir cur_path(QDir::currentPath() + "/" + input_name);
468 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
473 void
474 Main_GUI::absolute_output()
476 QString output_name = QDir::fromNativeSeparators(output_line->text());
477 if (!output_name.isEmpty()
478 && QDir::isRelativePath(output_name))
480 QDir cur_path(QDir::currentPath() + "/" + output_name);
481 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
486 void
487 Main_GUI::absolute_control()
489 QString control_name = QDir::fromNativeSeparators(control_line->text());
490 if (!control_name.isEmpty()
491 && QDir::isRelativePath(control_name))
493 QDir cur_path(QDir::currentPath() + "/" + control_name);
494 control_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
499 void
500 Main_GUI::check_number_set()
502 QString text = snapping_line->text();
503 QString qs;
505 // construct ASCII string from arbitrary Unicode data;
506 // the idea is to accept, say, CJK fullwidth digits also
507 for (int i = 0; i < text.size(); i++)
509 QChar c = text.at(i);
511 int digit = c.digitValue();
512 if (digit >= 0)
513 qs += QString::number(digit);
514 else if (c.isSpace())
515 qs += ' ';
516 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
517 // to the `-' key in some Japanese input methods
518 else if (c.category() == QChar::Punctuation_Dash
519 || c == QChar(0x30FC))
520 qs += '-';
521 // various Unicode COMMA characters,
522 // including representation forms
523 else if (c == QChar(',')
524 || c == QChar(0x055D)
525 || c == QChar(0x060C)
526 || c == QChar(0x07F8)
527 || c == QChar(0x1363)
528 || c == QChar(0x1802)
529 || c == QChar(0x1808)
530 || c == QChar(0x3001)
531 || c == QChar(0xA4FE)
532 || c == QChar(0xA60D)
533 || c == QChar(0xA6F5)
534 || c == QChar(0xFE10)
535 || c == QChar(0xFE11)
536 || c == QChar(0xFE50)
537 || c == QChar(0xFE51)
538 || c == QChar(0xFF0C)
539 || c == QChar(0xFF64))
540 qs += ',';
541 else
542 qs += c; // we do error handling below
545 if (x_height_snapping_exceptions)
546 number_set_free(x_height_snapping_exceptions);
548 QByteArray str = qs.toLocal8Bit();
549 const char* s = number_set_parse(str.constData(),
550 &x_height_snapping_exceptions,
551 6, 0x7FFF);
552 if (s && *s)
554 statusBar()->setStyleSheet("color: red;");
555 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
556 statusBar()->showMessage(
557 tr("allocation error"));
558 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
559 statusBar()->showMessage(
560 tr("invalid character (use digits, dashes, commas, and spaces)"));
561 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
562 statusBar()->showMessage(
563 tr("overflow"));
564 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
565 statusBar()->showMessage(
566 tr("invalid range (minimum is 6, maximum is 32767)"));
567 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
568 statusBar()->showMessage(
569 tr("overlapping ranges"));
570 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
571 statusBar()->showMessage(
572 tr("values und ranges must be specified in ascending order"));
574 snapping_line->setText(qs);
575 snapping_line->setFocus(Qt::OtherFocusReason);
576 snapping_line->setCursorPosition(s - str.constData());
578 x_height_snapping_exceptions = NULL;
580 else
582 // normalize if there is no error
583 char* new_str = number_set_show(x_height_snapping_exceptions,
584 6, 0x7FFF);
585 snapping_line->setText(new_str);
586 free(new_str);
591 void
592 Main_GUI::clear_status_bar()
594 statusBar()->clearMessage();
595 statusBar()->setStyleSheet("");
600 Main_GUI::check_filenames(const QString& input_name,
601 const QString& output_name,
602 const QString& control_name)
604 if (!QFile::exists(input_name))
606 QMessageBox::warning(
607 this,
608 "TTFautohint",
609 tr("The file %1 cannot be found.")
610 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
611 QMessageBox::Ok,
612 QMessageBox::Ok);
613 return 0;
616 if (input_name == output_name)
618 QMessageBox::warning(
619 this,
620 "TTFautohint",
621 tr("Input and output file names must be different."),
622 QMessageBox::Ok,
623 QMessageBox::Ok);
624 return 0;
627 // silently overwrite if watching is active
628 if (QFile::exists(output_name) && !timer->isActive())
630 int ret = QMessageBox::warning(
631 this,
632 "TTFautohint",
633 tr("The file %1 already exists.\n"
634 "Overwrite?")
635 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
636 QMessageBox::Yes | QMessageBox::No,
637 QMessageBox::No);
638 if (ret == QMessageBox::No)
639 return 0;
642 if (!control_name.isEmpty() && !QFile::exists(control_name))
644 QMessageBox::warning(
645 this,
646 "TTFautohint",
647 tr("The file %1 cannot be found.")
648 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name))),
649 QMessageBox::Ok,
650 QMessageBox::Ok);
651 return 0;
654 return 1;
659 Main_GUI::open_files(const QString& input_name,
660 FILE** in,
661 const QString& output_name,
662 FILE** out,
663 const QString& control_name,
664 FILE** control)
666 const int buf_len = 1024;
667 char buf[buf_len];
669 *in = fopen(qPrintable(input_name), "rb");
670 if (!*in)
672 strerror_r(errno, buf, buf_len);
673 QMessageBox::warning(
674 this,
675 "TTFautohint",
676 tr("The following error occurred while opening input file %1:\n")
677 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
678 + QString::fromLocal8Bit(buf),
679 QMessageBox::Ok,
680 QMessageBox::Ok);
681 return 0;
684 *out = fopen(qPrintable(output_name), "wb");
685 if (!*out)
687 strerror_r(errno, buf, buf_len);
688 QMessageBox::warning(
689 this,
690 "TTFautohint",
691 tr("The following error occurred while opening output file %1:\n")
692 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
693 + QString::fromLocal8Bit(buf),
694 QMessageBox::Ok,
695 QMessageBox::Ok);
696 return 0;
699 if (!control_name.isEmpty())
701 *control = fopen(qPrintable(control_name), "r");
702 if (!*control)
704 strerror_r(errno, buf, buf_len);
705 QMessageBox::warning(
706 this,
707 "TTFautohint",
708 tr("The following error occurred"
709 " while opening control instructions file %1:\n")
710 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name)))
711 + QString::fromLocal8Bit(buf),
712 QMessageBox::Ok,
713 QMessageBox::Ok);
714 return 0;
717 else
718 *control = NULL;
720 return 1;
724 void
725 Main_GUI::check_watch()
727 if (!watch_box->isChecked())
728 timer->stop();
729 // the timer gets started in the `run' function
733 void
734 Main_GUI::watch_files()
736 if (fileinfo_input_file.exists()
737 && fileinfo_input_file.isReadable()
738 && fileinfo_control_file.exists()
739 && fileinfo_control_file.isReadable())
741 QDateTime modified_input = fileinfo_input_file.lastModified();
742 QDateTime modified_control = fileinfo_control_file.lastModified();
743 if (modified_input > datetime_input_file
744 || modified_control > datetime_control_file)
745 run(); // this function sets `datetime_XXX'
747 else
748 run(); // let this routine handle all errors
752 extern "C" {
754 struct GUI_Progress_Data
756 long last_sfnt;
757 bool begin;
758 QProgressDialog* dialog;
762 #undef TRDOMAIN
763 #define TRDOMAIN "GuiProgress"
766 gui_progress(long curr_idx,
767 long num_glyphs,
768 long curr_sfnt,
769 long num_sfnts,
770 void* user)
772 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
774 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
776 data->dialog->setLabelText(Tr("Auto-hinting subfont %1 of %2"
777 " with %3 glyphs...")
778 .arg(curr_sfnt + 1)
779 .arg(num_sfnts)
780 .arg(num_glyphs));
782 if (curr_sfnt + 1 == num_sfnts)
784 data->dialog->setAutoReset(true);
785 data->dialog->setAutoClose(true);
787 else
789 data->dialog->setAutoReset(false);
790 data->dialog->setAutoClose(false);
793 data->last_sfnt = curr_sfnt;
794 data->begin = true;
797 if (data->begin)
799 if (num_sfnts == 1)
800 data->dialog->setLabelText(Tr("Auto-hinting %1 glyphs...")
801 .arg(num_glyphs));
802 data->dialog->setMaximum(num_glyphs - 1);
804 data->begin = false;
807 data->dialog->setValue(curr_idx);
809 if (data->dialog->wasCanceled())
810 return 1;
812 return 0;
816 struct GUI_Error_Data
818 Main_GUI* gui;
819 QLocale* locale;
820 QString output_name;
821 QString control_name;
822 int* ignore_restrictions_p;
823 bool retry;
827 #undef TRDOMAIN
828 #define TRDOMAIN "GuiError"
830 void
831 gui_error(TA_Error error,
832 const char* error_string,
833 unsigned int errlinenum,
834 const char* errline,
835 const char* errpos,
836 void* user)
838 GUI_Error_Data* data = static_cast<GUI_Error_Data*>(user);
839 QLocale* locale = data->locale; // for QUOTE_STRING_LITERAL
841 if (!error)
842 return;
844 if (error == TA_Err_Canceled)
846 else if (error == TA_Err_Invalid_FreeType_Version)
847 QMessageBox::critical(
848 data->gui,
849 "TTFautohint",
850 Tr("FreeType version 2.4.5 or higher is needed.\n"
851 "Are you perhaps using a wrong FreeType DLL?"),
852 QMessageBox::Ok,
853 QMessageBox::Ok);
854 else if (error == TA_Err_Invalid_Font_Type)
855 QMessageBox::warning(
856 data->gui,
857 "TTFautohint",
858 Tr("This font is not a valid font"
859 " in SFNT format with TrueType outlines.\n"
860 "In particular, CFF outlines are not supported."),
861 QMessageBox::Ok,
862 QMessageBox::Ok);
863 else if (error == TA_Err_Already_Processed)
864 QMessageBox::warning(
865 data->gui,
866 "TTFautohint",
867 Tr("This font has already been processed by TTFautohint."),
868 QMessageBox::Ok,
869 QMessageBox::Ok);
870 else if (error == TA_Err_Missing_Legal_Permission)
872 int yesno = QMessageBox::warning(
873 data->gui,
874 "TTFautohint",
875 Tr("Bit 1 in the %1 field of the %2 table is set:"
876 " This font must not be modified"
877 " without permission of the legal owner.\n"
878 "Do you have such a permission?")
879 .arg(QUOTE_STRING_LITERAL("fsType"))
880 .arg(QUOTE_STRING_LITERAL("OS/2")),
881 QMessageBox::Yes | QMessageBox::No,
882 QMessageBox::No);
883 if (yesno == QMessageBox::Yes)
885 *data->ignore_restrictions_p = true;
886 data->retry = true;
889 else if (error == TA_Err_Missing_Unicode_CMap)
890 QMessageBox::warning(
891 data->gui,
892 "TTFautohint",
893 Tr("The input font doesn't contain a Unicode character map.\n"
894 "Maybe you haven't set the %1 checkbox?")
895 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
896 QMessageBox::Ok,
897 QMessageBox::Ok);
898 else if (error == TA_Err_Missing_Symbol_CMap)
899 QMessageBox::warning(
900 data->gui,
901 "TTFautohint",
902 Tr("The input font does neither contain a symbol"
903 " nor a character map."),
904 QMessageBox::Ok,
905 QMessageBox::Ok);
906 else if (error == TA_Err_Missing_Glyph)
907 QMessageBox::warning(
908 data->gui,
909 "TTFautohint",
910 Tr("No glyph for a standard character"
911 " to derive standard width and height.\n"
912 "Please check the documentation for a list of"
913 " script-specific standard characters.\n"
914 "\n"
915 "Set the %1 checkbox if you want to circumvent this test.")
916 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
917 QMessageBox::Ok,
918 QMessageBox::Ok);
919 else if (error >= 0x200 && error < 0x300)
920 QMessageBox::warning(
921 data->gui,
922 "TTFautohint",
923 QString::fromLocal8Bit("%1:%2:%3: %4 (0x%5)<br>"
924 "<tt> %6<br>"
925 " %7</tt>")
926 .arg(data->control_name)
927 .arg(errlinenum)
928 .arg(int(errpos - errline + 1))
929 .arg(error_string)
930 .arg(error, 2, 16, QLatin1Char('0'))
931 .arg(errline)
932 .arg('^', int(errpos - errline + 1))
933 .replace(" ", "&nbsp;"),
934 QMessageBox::Ok,
935 QMessageBox::Ok);
936 else
937 QMessageBox::warning(
938 data->gui,
939 "TTFautohint",
940 Tr("Error code 0x%1 while autohinting font:\n")
941 .arg(error, 2, 16, QLatin1Char('0'))
942 + QString::fromLocal8Bit(error_string),
943 QMessageBox::Ok,
944 QMessageBox::Ok);
946 if (QFile::exists(data->output_name)
947 && remove(qPrintable(data->output_name)))
949 const int buf_len = 1024;
950 char buf[buf_len];
952 strerror_r(errno, buf, buf_len);
953 QMessageBox::warning(
954 data->gui,
955 "TTFautohint",
956 Tr("The following error occurred while removing output file %1:\n")
957 .arg(QUOTE_STRING(QDir::toNativeSeparators(data->output_name)))
958 + QString::fromLocal8Bit(buf),
959 QMessageBox::Ok,
960 QMessageBox::Ok);
964 } // extern "C"
967 void
968 Main_GUI::run()
970 statusBar()->clearMessage();
972 QString input_name = QDir::fromNativeSeparators(input_line->text());
973 QString output_name = QDir::fromNativeSeparators(output_line->text());
974 QString control_name = QDir::fromNativeSeparators(control_line->text());
975 if (!check_filenames(input_name, output_name, control_name))
977 timer->stop();
978 return;
981 // we need C file descriptors for communication with TTF_autohint
982 FILE* input;
983 FILE* output;
984 FILE* control;
986 again:
987 if (!open_files(input_name, &input,
988 output_name, &output,
989 control_name, &control))
991 timer->stop();
992 return;
995 QProgressDialog dialog;
996 dialog.setCancelButtonText(tr("Cancel"));
997 dialog.setMinimumDuration(1000);
998 dialog.setWindowModality(Qt::WindowModal);
1000 TA_Info_Func info_func = info;
1001 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
1002 GUI_Error_Data gui_error_data = {this, locale, output_name, control_name,
1003 &ignore_restrictions, false};
1005 fileinfo_input_file.setFile(input_name);
1006 fileinfo_control_file.setFile(control_name);
1008 Info_Data info_data;
1010 info_data.info_string = NULL; // must be deallocated after use
1011 info_data.info_string_wide = NULL; // must be deallocated after use
1012 info_data.info_string_len = 0;
1013 info_data.info_string_wide_len = 0;
1015 info_data.control_name = qPrintable(fileinfo_control_file.fileName());
1017 info_data.hinting_range_min = min_box->value();
1018 info_data.hinting_range_max = max_box->value();
1019 info_data.hinting_limit = no_limit_box->isChecked()
1021 : limit_box->value();
1023 info_data.gray_strong_stem_width = gray_box->isChecked();
1024 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
1025 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
1027 info_data.increase_x_height = no_increase_box->isChecked()
1029 : increase_box->value();
1030 info_data.x_height_snapping_exceptions_string =
1031 qPrintable(x_height_snapping_exceptions_string);
1032 info_data.fallback_stem_width = default_stem_width_box->isChecked()
1034 : stem_width_box->value();
1036 info_data.windows_compatibility = wincomp_box->isChecked();
1037 info_data.adjust_subglyphs = adjust_box->isChecked();
1038 info_data.hint_composites = hint_box->isChecked();
1039 info_data.symbol = symbol_box->isChecked();
1040 info_data.no_info = info_box->currentIndex() == 0;
1041 info_data.detailed_info = info_box->currentIndex() == 2;
1042 info_data.dehint = dehint_box->isChecked();
1043 info_data.TTFA_info = TTFA_box->isChecked();
1045 strncpy(info_data.default_script,
1046 script_names[default_box->currentIndex()].tag,
1047 sizeof (info_data.default_script));
1048 strncpy(info_data.fallback_script,
1049 script_names[fallback_box->currentIndex()].tag,
1050 sizeof (info_data.fallback_script));
1052 if (info_box->currentIndex())
1054 int ret = build_version_string(&info_data);
1055 if (ret == 1)
1056 QMessageBox::information(
1057 this,
1058 "TTFautohint",
1059 tr("Can't allocate memory for <b>TTFautohint</b> options string"
1060 " in <i>name</i> table."),
1061 QMessageBox::Ok,
1062 QMessageBox::Ok);
1063 else if (ret == 2)
1064 QMessageBox::information(
1065 this,
1066 "TTFautohint",
1067 tr("<b>TTFautohint</b> options string"
1068 " in <i>name</i> table too long."),
1069 QMessageBox::Ok,
1070 QMessageBox::Ok);
1073 if (info_data.symbol
1074 && info_data.fallback_stem_width
1075 && !strcmp(info_data.fallback_script, "none"))
1076 QMessageBox::information(
1077 this,
1078 "TTFautohint",
1079 tr("Setting a fallback stem width for a symbol font"
1080 " without setting a fallback script has no effect."),
1081 QMessageBox::Ok,
1082 QMessageBox::Ok);
1084 datetime_input_file = fileinfo_input_file.lastModified();
1085 datetime_control_file = fileinfo_control_file.lastModified();
1087 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
1089 TA_Error error =
1090 TTF_autohint("in-file, out-file, control-file,"
1091 "hinting-range-min, hinting-range-max,"
1092 "hinting-limit,"
1093 "gray-strong-stem-width,"
1094 "gdi-cleartype-strong-stem-width,"
1095 "dw-cleartype-strong-stem-width,"
1096 "progress-callback, progress-callback-data,"
1097 "error-callback, error-callback-data,"
1098 "info-callback, info-callback-data,"
1099 "ignore-restrictions,"
1100 "windows-compatibility,"
1101 "adjust-subglyphs,"
1102 "hint-composites,"
1103 "increase-x-height,"
1104 "x-height-snapping-exceptions, fallback-stem-width,"
1105 "default-script, fallback-script,"
1106 "symbol, dehint, TTFA-info",
1107 input, output, control,
1108 info_data.hinting_range_min, info_data.hinting_range_max,
1109 info_data.hinting_limit,
1110 info_data.gray_strong_stem_width,
1111 info_data.gdi_cleartype_strong_stem_width,
1112 info_data.dw_cleartype_strong_stem_width,
1113 gui_progress, &gui_progress_data,
1114 gui_error, &gui_error_data,
1115 info_func, &info_data,
1116 ignore_restrictions,
1117 info_data.windows_compatibility,
1118 info_data.adjust_subglyphs,
1119 info_data.hint_composites,
1120 info_data.increase_x_height,
1121 snapping_string.constData(), info_data.fallback_stem_width,
1122 info_data.default_script, info_data.fallback_script,
1123 info_data.symbol, info_data.dehint, info_data.TTFA_info);
1125 if (info_box->currentIndex())
1127 free(info_data.info_string);
1128 free(info_data.info_string_wide);
1131 fclose(input);
1132 fclose(output);
1133 if (control)
1134 fclose(control);
1136 if (error)
1138 // retry if there is a user request to do so (handled in `gui_error')
1139 if (gui_error_data.retry)
1140 goto again;
1142 timer->stop();
1144 else
1146 statusBar()->showMessage(tr("Auto-hinting finished")
1147 + " ("
1148 + QDateTime::currentDateTime()
1149 .toString(Qt::TextDate)
1150 + ").");
1152 // we have successfully processed a file;
1153 // start file watching now if requested
1154 if (watch_box->isChecked())
1155 timer->start();
1160 // XXX distances are specified in pixels,
1161 // making the layout dependent on the output device resolution
1162 void
1163 Main_GUI::create_layout(bool horizontal_layout)
1166 // file stuff
1168 QCompleter* completer = new QCompleter(this);
1169 QFileSystemModel* model = new QFileSystemModel(completer);
1170 model->setRootPath(QDir::rootPath());
1171 completer->setModel(model);
1173 input_label = new QLabel(tr("&Input File:"));
1174 input_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1175 input_button = new QPushButton(tr("Browse..."));
1176 input_label->setBuddy(input_line);
1177 // enforce rich text to get nice word wrapping
1178 input_label->setToolTip(
1179 tr("<b></b>The input file, either a TrueType font (TTF),"
1180 " TrueType collection (TTC), or a TrueType-based OpenType font."));
1181 input_line->setCompleter(completer);
1183 output_label = new QLabel(tr("&Output File:"));
1184 output_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1185 output_button = new QPushButton(tr("Browse..."));
1186 output_label->setBuddy(output_line);
1187 output_label->setToolTip(
1188 tr("<b></b>The output file, which will be essentially identical"
1189 " to the input font but will contain new, generated hints."));
1190 output_line->setCompleter(completer);
1192 control_label = new QLabel(tr("Control Instructions Fil&e:"));
1193 control_line = new Drag_Drop_Line_Edit(DRAG_DROP_ANY);
1194 control_button = new QPushButton(tr("Browse..."));
1195 control_label->setBuddy(control_line);
1196 control_label->setToolTip(
1197 tr("<p>An optional control instructions file to tweak hinting."
1198 " This text file contains entries"
1199 " of one of the following syntax forms"
1200 " (with brackets indicating optional elements).<br>"
1201 "&nbsp;<br>"
1202 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1203 "&nbsp;&nbsp;<i>glyph-id</i>"
1204 "&nbsp;&nbsp;<tt>left</tt>&nbsp;|&nbsp;<tt>right</tt>&nbsp;<i>points</i>"
1205 "&nbsp;&nbsp;[&nbsp;<tt>(</tt><i>left-offset</i><tt>,"
1206 "</tt><i>right-offset</i><tt>)</tt>&nbsp;]<br>"
1207 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1208 "&nbsp;&nbsp;<i>glyph-id</i>"
1209 "&nbsp;&nbsp;<tt>nodir</tt>&nbsp;<i>points</i><br>"
1210 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1211 "&nbsp;&nbsp;<i>glyph-id</i>"
1212 "&nbsp;&nbsp;<tt>touch</tt>&nbsp;|&nbsp;<tt>point</tt>&nbsp;<i>points</i>"
1213 "&nbsp;&nbsp;[&nbsp;<tt>xshift</tt>&nbsp;<i>shift</i>&nbsp;]"
1214 "&nbsp;&nbsp;[&nbsp;<tt>yshift</tt>&nbsp;<i>shift</i>&nbsp;]"
1215 "&nbsp;&nbsp;<tt>@</tt>&nbsp;<i>ppems</i><br>"
1216 "&nbsp;<br>"
1217 "<i>subfont-idx</i> gives the subfont index in a TTC,"
1218 " <i>glyph-id</i> is a glyph name or index.<br>"
1219 "<tt>left</tt> (<tt>right</tt>) creates one-point segments"
1220 " with direction left (right), possibly having a width (in font units)"
1221 " given by <i>left-offset</i> and <i>right-offset</i>"
1222 " relative to the corresponding points.<br>"
1223 "<tt>nodir</tt> removes points from horizontal segments,"
1224 " making them <i>weak</i> points.<br>"
1225 "<tt>touch </tt> (<tt>point</tt>) defines delta exceptions"
1226 " to be applied before (after) the final"
1227 " <tt>IUP</tt> bytecode instructions."
1228 " <tt>touch</tt> also touches points, making them <i>strong</i>."
1229 " In ClearType mode, <tt>point</tt> and <tt>xshift</tt>"
1230 " have no effect.<br>"
1231 "x and y <i>shift</i> values are in the range [-1;1],"
1232 " rounded to multiples of 1/8px.<br>"
1233 "<i>points</i> and <i>ppems</i> are ranges for point indices"
1234 " and ppem values as with x&nbsp;height snapping exceptions.<br>"
1235 "Keywords <tt>left</tt>, <tt>right</tt>, <tt>nodir</tt>,"
1236 " <tt>point</tt>, <tt>touch</tt>, <tt>xshift</tt>, and"
1237 " <tt>yshift</tt> can be abbreviated as <tt>l</tt>, <tt>r</tt>,"
1238 " <tt>n</tt>, <tt>p</tt>, <tt>t</tt>, <tt>x</tt>, and <tt>y</tt>,"
1239 " respectively.<br>"
1240 "Control instruction entries are separated"
1241 " by character&nbsp;<tt>;</tt> or by a newline.<br>"
1242 "A trailing character&nbsp;<tt>\\</tt> continues the current line"
1243 " on the next line.<br>"
1244 "<tt>#</tt> starts a line comment, which gets ignored."
1245 " Empty lines are ignored, too.</p>"
1247 "Examples:<br>"
1248 "&nbsp;&nbsp;<tt>Q left 38 (-70,20)</tt><br>"
1249 "&nbsp;&nbsp;<tt>Adieresis touch 3-6 yshift 0.25 @ 13</tt>"));
1250 control_line->setCompleter(completer);
1253 // minmax controls
1255 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
1256 min_box = new QSpinBox;
1257 min_label->setBuddy(min_box);
1258 min_label->setToolTip(
1259 tr("The minimum PPEM value of the range for which"
1260 " <b>TTFautohint</b> computes <i>hint sets</i>."
1261 " A hint set for a given PPEM value hints this size optimally."
1262 " The larger the range, the more hint sets are considered,"
1263 " usually increasing the size of the bytecode.<br>"
1264 "Note that changing this range doesn't influence"
1265 " the <i>gasp</i> table:"
1266 " Hinting is enabled for all sizes."));
1267 min_box->setKeyboardTracking(false);
1268 min_box->setRange(2, 10000);
1270 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
1271 max_box = new QSpinBox;
1272 max_label->setBuddy(max_box);
1273 max_label->setToolTip(
1274 tr("The maximum PPEM value of the range for which"
1275 " <b>TTFautohint</b> computes <i>hint sets</i>."
1276 " A hint set for a given PPEM value hints this size optimally."
1277 " The larger the range, the more hint sets are considered,"
1278 " usually increasing the size of the bytecode.<br>"
1279 "Note that changing this range doesn't influence"
1280 " the <i>gasp</i> table:"
1281 " Hinting is enabled for all sizes."));
1282 max_box->setKeyboardTracking(false);
1283 max_box->setRange(2, 10000);
1286 // OpenType default script
1288 default_label = new QLabel(tr("Defa&ult Script:"));
1289 default_box = new QComboBox;
1290 default_label->setBuddy(default_box);
1291 default_label->setToolTip(
1292 tr("This sets the default script for OpenType features:"
1293 " After applying all features that are handled specially"
1294 " (for example small caps or superscript glyphs),"
1295 " <b>TTFautohint</b> uses this value for the remaining features."));
1296 for (int i = 0; script_names[i].tag; i++)
1298 // XXX: how to provide translations?
1299 default_box->insertItem(i,
1300 QString("%1 (%2)")
1301 .arg(script_names[i].tag)
1302 .arg(script_names[i].description));
1306 // hinting and fallback controls
1308 fallback_label = new QLabel(tr("Fallback &Script:"));
1309 fallback_box = new QComboBox;
1310 fallback_label->setBuddy(fallback_box);
1311 fallback_label->setToolTip(
1312 tr("This sets the fallback script for glyphs"
1313 " that <b>TTFautohint</b> can't map to a script automatically."));
1314 for (int i = 0; script_names[i].tag; i++)
1316 // XXX: how to provide translations?
1317 fallback_box->insertItem(i,
1318 QString("%1 (%2)")
1319 .arg(script_names[i].tag)
1320 .arg(script_names[i].description));
1324 // hinting limit
1326 limit_label_text_with_key = tr("Hinting &Limit:");
1327 limit_label_text = tr("Hinting Limit:");
1328 limit_label = new QLabel(limit_label_text_with_key);
1329 limit_box = new QSpinBox;
1330 limit_label->setBuddy(limit_box);
1331 limit_label->setToolTip(
1332 tr("Switch off hinting for PPEM values exceeding this limit."
1333 " Changing this value does not influence the size of the bytecode.<br>"
1334 "Note that <b>TTFautohint</b> handles this feature"
1335 " in the output font's bytecode and not in the <i>gasp</i> table."));
1336 limit_box->setKeyboardTracking(false);
1337 limit_box->setRange(2, 10000);
1339 no_limit_box_text_with_key = tr("No Hinting &Limit");
1340 no_limit_box_text = tr("No Hinting Limit");
1341 no_limit_box = new QCheckBox(no_limit_box_text, this);
1342 no_limit_box->setToolTip(
1343 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
1344 " to the bytecode.<br>"
1345 "For testing only."));
1348 // x height increase limit
1350 increase_label_text_with_key = tr("x Height In&crease Limit:");
1351 increase_label_text = tr("x Height Increase Limit:");
1352 increase_label = new QLabel(increase_label_text_with_key);
1353 increase_box = new QSpinBox;
1354 increase_label->setBuddy(increase_box);
1355 increase_label->setToolTip(
1356 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
1357 " where <i>n</i> is the value selected by this spin box,"
1358 " round up the font's x&nbsp;height much more often than normally.<br>"
1359 "Use this if holes in letters like <i>e</i> get filled,"
1360 " for example."));
1361 increase_box->setKeyboardTracking(false);
1362 increase_box->setRange(6, 10000);
1364 no_increase_box_text_with_key = tr("No x Height In&crease");
1365 no_increase_box_text = tr("No x Height Increase");
1366 no_increase_box = new QCheckBox(no_increase_box_text, this);
1367 no_increase_box->setToolTip(
1368 tr("If switched on,"
1369 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
1372 // x height snapping exceptions
1374 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
1375 snapping_line = new Tooltip_Line_Edit;
1376 snapping_label->setBuddy(snapping_line);
1377 snapping_label->setToolTip(
1378 tr("<p>A list of comma separated PPEM values or value ranges"
1379 " at which no x&nbsp;height snapping shall be applied"
1380 " (x&nbsp;height snapping usually slightly increases"
1381 " the size of all glyphs).</p>"
1383 "Examples:<br>"
1384 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1385 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1386 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1387 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1390 // fallback stem width
1392 stem_width_label_text_with_key = tr("Fallbac&k Stem Width:");
1393 stem_width_label_text = tr("Fallback Stem Width:");
1394 stem_width_label = new QLabel(stem_width_label_text_with_key);
1395 stem_width_box = new QSpinBox;
1396 stem_width_label->setBuddy(stem_width_box);
1397 stem_width_label->setToolTip(
1398 tr("Set horizontal stem width (in font units) for all scripts"
1399 " that lack proper standard characters in the font.<br>"
1400 "If not set, <b>TTFautohint</b> uses a hard-coded default value."));
1401 stem_width_box->setKeyboardTracking(false);
1402 stem_width_box->setRange(1, 10000);
1404 default_stem_width_box_text_with_key = tr("Default Fallbac&k Stem Width");
1405 default_stem_width_box_text = tr("Default Fallback Stem Width");
1406 default_stem_width_box = new QCheckBox(default_stem_width_box_text, this);
1407 default_stem_width_box->setToolTip(
1408 tr("If switched on, <b>TTFautohint</b> uses a default value"
1409 " for the fallback stem width (50 font units at 2048 UPEM)."));
1412 // flags
1414 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1415 wincomp_box->setToolTip(
1416 tr("If switched on, add two artificial blue zones positioned at the"
1417 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1418 " (from the font's <i>OS/2</i> table)."
1419 " This option, usually in combination"
1420 " with value <tt>-</tt> (a single dash)"
1421 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1422 " should be used if those two <i>OS/2</i> values are tight,"
1423 " and you are experiencing clipping during rendering."));
1425 adjust_box = new QCheckBox(tr("Ad&just Subglyphs"), this);
1426 adjust_box->setToolTip(
1427 tr("If switched on, the original bytecode of the input font"
1428 " gets applied (at EM size, usually 2048ppem)"
1429 " to derive the glyph outlines for <b>TTFautohint</b>.<br>"
1430 "Use this option only if subglyphs"
1431 " are incorrectly scaled and shifted.<br>"
1432 "Note that the original bytecode will always be discarded."));
1434 hint_box = new QCheckBox(tr("Hint Co&mposites")
1435 + " ", this); // make label wider
1436 hint_box->setToolTip(
1437 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1438 " as a whole, including subglyphs."
1439 " Otherwise, glyph components get hinted separately.<br>"
1440 "Deactivating this flag reduces the bytecode size enormously,"
1441 " however, it might yield worse results."));
1443 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1444 symbol_box->setToolTip(
1445 tr("If switched on, <b>TTFautohint</b> accepts fonts"
1446 " that don't contain a single standard character"
1447 " for any of the supported scripts.<br>"
1448 "Use this for symbol or dingbat fonts, for example."));
1450 dehint_box = new QCheckBox(tr("&Dehint"), this);
1451 dehint_box->setToolTip(
1452 tr("If set, remove all hints from the font.<br>"
1453 "For testing only."));
1455 info_label = new QLabel(tr("ttf&autohint Info:"));
1456 info_box = new QComboBox;
1457 info_label->setBuddy(info_box);
1458 info_label->setToolTip(
1459 tr("Specify which information about <b>TTFautohint</b>"
1460 " and its calling parameters gets added to the version string(s)"
1461 " (name ID&nbsp;5) in the <i>name</i> table."));
1462 info_box->insertItem(0, tr("None"));
1463 info_box->insertItem(1, tr("Version"));
1464 info_box->insertItem(2, tr("Version and Parameters"));
1466 TTFA_box = new QCheckBox(tr("Add TTFA Info Ta&ble"), this);
1467 TTFA_box->setToolTip(
1468 tr("If switched on, an SFNT table called <i>TTFA</i>"
1469 " gets added to the output font,"
1470 " holding a dump of all parameters."
1471 " In particular, it lists all control instructions."));
1474 // stem width and positioning
1476 stem_label = new QLabel(tr("Stron&g Stem Width and Positioning:"));
1477 stem_label->setToolTip(
1478 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1479 " that can be selected for various hinting modes."
1481 "<p><i>strong</i> (checkbox set):"
1482 " Position horizontal stems and snap stem widths"
1483 " to integer pixel values. While making the output look crisper,"
1484 " outlines become more distorted.</p>"
1486 "<p><i>smooth</i> (checkbox not set):"
1487 " Use discrete values for horizontal stems and stem widths."
1488 " This only slightly increases the contrast"
1489 " but avoids larger outline distortion.</p>"));
1491 gray_box = new QCheckBox(tr("Grayscale"), this);
1492 gray_box->setToolTip(
1493 tr("<b></b>Grayscale rendering, no ClearType activated."));
1494 stem_label->setBuddy(gray_box);
1496 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1497 gdi_box->setToolTip(
1498 tr("GDI ClearType rendering,"
1499 " introduced in 2000 for Windows XP.<br>"
1500 "The rasterizer version (as returned by the"
1501 " GETINFO bytecode instruction) is in the range"
1502 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1503 "Along the vertical axis, this mode behaves like B/W rendering."));
1505 dw_box = new QCheckBox(tr("DW ClearType"), this);
1506 dw_box->setToolTip(
1507 tr("DirectWrite ClearType rendering,"
1508 " introduced in 2008 for Windows Vista.<br>"
1509 "The rasterizer version (as returned by the"
1510 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1511 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1512 "Smooth rendering along the vertical axis."));
1515 // running
1517 watch_box = new QCheckBox(tr("&Watch Input Files"), this);
1518 watch_box->setToolTip(
1519 tr("If switched on, <b>TTFautohint</b> automatically re-runs"
1520 " the hinting process as soon as an input file"
1521 " (either the font or the control instructions file) is modified.<br>"
1522 "Pressing the %1 button starts watching.<br>"
1523 "If an error occurs, watching stops and must be restarted"
1524 " with the %1 button.")
1525 .arg(QUOTE_STRING_LITERAL(tr("Run"))));
1527 run_button = new QPushButton(" "
1528 + tr("&Run")
1529 + " "); // make label wider
1531 if (horizontal_layout)
1532 create_horizontal_layout();
1533 else
1534 create_vertical_layout();
1538 // XXX distances are specified in pixels,
1539 // making the layout dependent on the output device resolution
1540 void
1541 Main_GUI::create_vertical_layout()
1543 // top area
1544 QGridLayout* file_layout = new QGridLayout;
1546 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1547 file_layout->addWidget(input_line, 0, 1);
1548 file_layout->addWidget(input_button, 0, 2);
1550 file_layout->setRowStretch(1, 1);
1552 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1553 file_layout->addWidget(output_line, 2, 1);
1554 file_layout->addWidget(output_button, 2, 2);
1556 file_layout->setRowStretch(3, 1);
1558 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1559 file_layout->addWidget(control_line, 4, 1);
1560 file_layout->addWidget(control_button, 4, 2);
1562 // bottom area
1563 QGridLayout* run_layout = new QGridLayout;
1565 run_layout->addWidget(watch_box, 0, 1);
1566 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1567 run_layout->setColumnStretch(0, 1);
1568 run_layout->setColumnStretch(2, 2);
1571 // the whole gui
1573 QGridLayout* gui_layout = new QGridLayout;
1574 QFrame* hline = new QFrame;
1575 hline->setFrameShape(QFrame::HLine);
1576 int row = 0; // this counter simplifies inserting new items
1578 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1579 gui_layout->setRowStretch(row++, 1);
1581 gui_layout->addLayout(file_layout, row, 0, row, -1);
1582 gui_layout->setRowStretch(row++, 1);
1584 gui_layout->addWidget(hline, row, 0, row, -1);
1585 gui_layout->setRowStretch(row++, 1);
1587 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1588 gui_layout->setRowStretch(row++, 1);
1590 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1591 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1592 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1593 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1595 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1596 gui_layout->setRowStretch(row++, 1);
1598 gui_layout->addWidget(default_label, row, 0, Qt::AlignRight);
1599 gui_layout->addWidget(default_box, row++, 1, Qt::AlignLeft);
1600 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1601 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1603 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1604 gui_layout->setRowStretch(row++, 1);
1606 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1607 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1608 gui_layout->addWidget(no_limit_box, row++, 1);
1610 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1611 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1612 gui_layout->addWidget(no_increase_box, row++, 1);
1614 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1615 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1617 gui_layout->addWidget(stem_width_label, row, 0, Qt::AlignRight);
1618 gui_layout->addWidget(stem_width_box, row++, 1, Qt::AlignLeft);
1619 gui_layout->addWidget(default_stem_width_box, row++, 1);
1621 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1622 gui_layout->setRowStretch(row++, 1);
1624 gui_layout->addWidget(wincomp_box, row++, 1);
1625 gui_layout->addWidget(adjust_box, row++, 1);
1626 gui_layout->addWidget(hint_box, row++, 1);
1627 gui_layout->addWidget(symbol_box, row++, 1);
1628 gui_layout->addWidget(dehint_box, row++, 1);
1630 gui_layout->addWidget(info_label, row, 0, Qt::AlignRight);
1631 gui_layout->addWidget(info_box, row++, 1, Qt::AlignLeft);
1632 gui_layout->addWidget(TTFA_box, row++, 1);
1634 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1635 gui_layout->setRowStretch(row++, 1);
1637 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1638 gui_layout->addWidget(gray_box, row++, 1);
1639 gui_layout->addWidget(gdi_box, row++, 1);
1640 gui_layout->addWidget(dw_box, row++, 1);
1642 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1643 gui_layout->setRowStretch(row++, 1);
1645 gui_layout->addLayout(run_layout, row, 0, row, -1);
1647 // create dummy widget to register layout
1648 QWidget* main_widget = new QWidget;
1649 main_widget->setLayout(gui_layout);
1650 setCentralWidget(main_widget);
1651 setWindowTitle("TTFautohint");
1655 // XXX distances are specified in pixels,
1656 // making the layout dependent on the output device resolution
1657 void
1658 Main_GUI::create_horizontal_layout()
1660 // top area
1661 QGridLayout* file_layout = new QGridLayout;
1663 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1664 file_layout->addWidget(input_line, 0, 1);
1665 file_layout->addWidget(input_button, 0, 2);
1667 file_layout->setRowStretch(1, 1);
1669 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1670 file_layout->addWidget(output_line, 2, 1);
1671 file_layout->addWidget(output_button, 2, 2);
1673 file_layout->setRowStretch(3, 1);
1675 file_layout->addWidget(control_label, 4, 0, Qt::AlignRight);
1676 file_layout->addWidget(control_line, 4, 1);
1677 file_layout->addWidget(control_button, 4, 2);
1679 // bottom area
1680 QGridLayout* run_layout = new QGridLayout;
1682 run_layout->addWidget(watch_box, 0, 1);
1683 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1684 run_layout->setColumnStretch(0, 2);
1685 run_layout->setColumnStretch(2, 3);
1686 run_layout->setColumnStretch(4, 1);
1689 // the whole gui
1691 QGridLayout* gui_layout = new QGridLayout;
1692 QFrame* hline = new QFrame;
1693 hline->setFrameShape(QFrame::HLine);
1694 int row = 0; // this counter simplifies inserting new items
1696 // margin
1697 gui_layout->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1698 gui_layout->setColumnStretch(0, 1);
1700 // left
1701 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1702 gui_layout->setRowStretch(row++, 1);
1704 gui_layout->addLayout(file_layout, row, 0, row, -1);
1705 gui_layout->setRowStretch(row++, 1);
1707 gui_layout->addWidget(hline, row, 0, row, -1);
1708 gui_layout->setRowStretch(row++, 1);
1710 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1711 gui_layout->setRowStretch(row++, 1);
1713 gui_layout->addWidget(min_label, row, 1, Qt::AlignRight);
1714 gui_layout->addWidget(min_box, row++, 2, Qt::AlignLeft);
1715 gui_layout->addWidget(max_label, row, 1, Qt::AlignRight);
1716 gui_layout->addWidget(max_box, row++, 2, Qt::AlignLeft);
1718 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1719 gui_layout->setRowStretch(row++, 1);
1721 gui_layout->addWidget(default_label, row, 1, Qt::AlignRight);
1722 gui_layout->addWidget(default_box, row++, 2, Qt::AlignLeft);
1723 gui_layout->addWidget(fallback_label, row, 1, Qt::AlignRight);
1724 gui_layout->addWidget(fallback_box, row++, 2, Qt::AlignLeft);
1726 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1727 gui_layout->setRowStretch(row++, 1);
1729 gui_layout->addWidget(limit_label, row, 1, Qt::AlignRight);
1730 gui_layout->addWidget(limit_box, row++, 2, Qt::AlignLeft);
1731 gui_layout->addWidget(no_limit_box, row++, 2);
1733 gui_layout->addWidget(increase_label, row, 1, Qt::AlignRight);
1734 gui_layout->addWidget(increase_box, row++, 2, Qt::AlignLeft);
1735 gui_layout->addWidget(no_increase_box, row++, 2);
1737 gui_layout->addWidget(snapping_label, row, 1, Qt::AlignRight);
1738 gui_layout->addWidget(snapping_line, row++, 2, Qt::AlignLeft);
1740 gui_layout->addWidget(stem_width_label, row, 1, Qt::AlignRight);
1741 gui_layout->addWidget(stem_width_box, row++, 2, Qt::AlignLeft);
1742 gui_layout->addWidget(default_stem_width_box, row++, 2);
1744 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1745 gui_layout->setRowStretch(row++, 1);
1747 gui_layout->addLayout(run_layout, row, 0, row, -1);
1749 // column separator
1750 gui_layout->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1751 gui_layout->setColumnStretch(3, 1);
1753 // right
1754 row = 4;
1755 gui_layout->addWidget(wincomp_box, row++, 4);
1756 gui_layout->addWidget(adjust_box, row++, 4);
1757 gui_layout->addWidget(hint_box, row++, 4);
1758 gui_layout->addWidget(symbol_box, row++, 4);
1759 gui_layout->addWidget(dehint_box, row++, 4);
1761 gui_layout->addWidget(info_label, row, 3, Qt::AlignRight);
1762 gui_layout->addWidget(info_box, row++, 4, Qt::AlignLeft);
1763 gui_layout->addWidget(TTFA_box, row++, 4);
1765 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1766 gui_layout->setRowStretch(row++, 1);
1768 gui_layout->addWidget(stem_label, row++, 4);
1770 QGridLayout* stem_layout = new QGridLayout;
1771 stem_layout->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1772 stem_layout->addWidget(gray_box, 0, 1);
1773 stem_layout->addWidget(gdi_box, 1, 1);
1774 stem_layout->addWidget(dw_box, 2, 1);
1776 gui_layout->addLayout(stem_layout, row, 4, 3, 1);
1777 row += 3;
1779 // margin
1780 gui_layout->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1781 gui_layout->setColumnStretch(5, 1);
1783 // create dummy widget to register layout
1784 QWidget* main_widget = new QWidget;
1785 main_widget->setLayout(gui_layout);
1786 setCentralWidget(main_widget);
1787 setWindowTitle("TTFautohint");
1791 void
1792 Main_GUI::create_connections()
1794 connect(input_button, SIGNAL(clicked()), this,
1795 SLOT(browse_input()));
1796 connect(output_button, SIGNAL(clicked()), this,
1797 SLOT(browse_output()));
1798 connect(control_button, SIGNAL(clicked()), this,
1799 SLOT(browse_control()));
1801 connect(input_line, SIGNAL(textChanged(QString)), this,
1802 SLOT(check_run()));
1803 connect(output_line, SIGNAL(textChanged(QString)), this,
1804 SLOT(check_run()));
1806 connect(input_line, SIGNAL(editingFinished()), this,
1807 SLOT(absolute_input()));
1808 connect(output_line, SIGNAL(editingFinished()), this,
1809 SLOT(absolute_output()));
1810 connect(control_line, SIGNAL(editingFinished()), this,
1811 SLOT(absolute_control()));
1813 connect(min_box, SIGNAL(valueChanged(int)), this,
1814 SLOT(check_min()));
1815 connect(max_box, SIGNAL(valueChanged(int)), this,
1816 SLOT(check_max()));
1818 connect(limit_box, SIGNAL(valueChanged(int)), this,
1819 SLOT(check_limit()));
1820 connect(no_limit_box, SIGNAL(clicked()), this,
1821 SLOT(check_no_limit()));
1823 connect(no_increase_box, SIGNAL(clicked()), this,
1824 SLOT(check_no_increase()));
1826 connect(snapping_line, SIGNAL(editingFinished()), this,
1827 SLOT(check_number_set()));
1828 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1829 SLOT(clear_status_bar()));
1831 connect(default_stem_width_box, SIGNAL(clicked()), this,
1832 SLOT(check_default_stem_width()));
1834 connect(dehint_box, SIGNAL(clicked()), this,
1835 SLOT(check_dehint()));
1837 connect(timer, SIGNAL(timeout()), this,
1838 SLOT(watch_files()));
1840 connect(watch_box, SIGNAL(clicked()), this,
1841 SLOT(check_watch()));
1843 connect(run_button, SIGNAL(clicked()), this,
1844 SLOT(run()));
1848 void
1849 Main_GUI::create_actions()
1851 exit_act = new QAction(tr("E&xit"), this);
1852 exit_act->setShortcuts(QKeySequence::Quit);
1853 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1855 about_act = new QAction(tr("&About"), this);
1856 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1858 about_Qt_act = new QAction(tr("About &Qt"), this);
1859 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1863 void
1864 Main_GUI::create_menus()
1866 file_menu = menuBar()->addMenu(tr("&File"));
1867 file_menu->addAction(exit_act);
1869 help_menu = menuBar()->addMenu(tr("&Help"));
1870 help_menu->addAction(about_act);
1871 help_menu->addAction(about_Qt_act);
1875 void
1876 Main_GUI::create_status_bar()
1878 statusBar()->showMessage("");
1882 void
1883 Main_GUI::set_defaults()
1885 min_box->setValue(hinting_range_min);
1886 max_box->setValue(hinting_range_max);
1888 default_box->setCurrentIndex(default_script_idx);
1889 fallback_box->setCurrentIndex(fallback_script_idx);
1891 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1892 // handle command line option `--hinting-limit=0'
1893 if (!hinting_limit)
1895 hinting_limit = max_box->value();
1896 no_limit_box->setChecked(true);
1899 increase_box->setValue(increase_x_height ? increase_x_height
1900 : TA_INCREASE_X_HEIGHT);
1901 // handle command line option `--increase-x-height=0'
1902 if (!increase_x_height)
1904 increase_x_height = TA_INCREASE_X_HEIGHT;
1905 no_increase_box->setChecked(true);
1908 snapping_line->setText(x_height_snapping_exceptions_string);
1910 if (fallback_stem_width)
1911 stem_width_box->setValue(fallback_stem_width);
1912 else
1914 stem_width_box->setValue(50);
1915 default_stem_width_box->setChecked(true);
1918 if (windows_compatibility)
1919 wincomp_box->setChecked(true);
1920 if (adjust_subglyphs)
1921 adjust_box->setChecked(true);
1922 if (hint_composites)
1923 hint_box->setChecked(true);
1924 if (symbol)
1925 symbol_box->setChecked(true);
1926 if (dehint)
1927 dehint_box->setChecked(true);
1928 if (no_info)
1929 info_box->setCurrentIndex(0);
1930 else if (detailed_info)
1931 info_box->setCurrentIndex(2);
1932 else
1933 info_box->setCurrentIndex(1);
1934 if (TTFA_info)
1935 TTFA_box->setChecked(true);
1937 if (gray_strong_stem_width)
1938 gray_box->setChecked(true);
1939 if (gdi_cleartype_strong_stem_width)
1940 gdi_box->setChecked(true);
1941 if (dw_cleartype_strong_stem_width)
1942 dw_box->setChecked(true);
1944 run_button->setEnabled(false);
1946 check_min();
1947 check_max();
1948 check_limit();
1950 check_no_limit();
1951 check_no_increase();
1952 check_number_set();
1954 // do this last since it disables almost everything
1955 check_dehint();
1959 void
1960 Main_GUI::read_settings()
1962 QSettings settings;
1963 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1964 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1965 // resize(size);
1966 // move(pos);
1970 void
1971 Main_GUI::write_settings()
1973 QSettings settings;
1974 // settings.setValue("pos", pos());
1975 // settings.setValue("size", size());
1978 // end of maingui.cpp