Synchronize front-ends with recent library changes.
[ttfautohint.git] / frontend / maingui.cpp
blobad00a962c31b4cf4725dc5adb8ae941e366803fb
1 // maingui.cpp
3 // Copyright (C) 2012-2013 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>
20 #include <QtGui>
22 #include "info.h"
23 #include "maingui.h"
25 #include <ttfautohint.h>
28 // XXX Qt 4.8 bug: locale->quoteString("foo")
29 // inserts wrongly encoded quote characters
30 // into rich text QString
31 #if HAVE_QT_QUOTESTRING
32 # define QUOTE_STRING(x) locale->quoteString(x)
33 # define QUOTE_STRING_LITERAL(x) locale->quoteString(x)
34 #else
35 # define QUOTE_STRING(x) "\"" + x + "\""
36 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
37 #endif
40 // the available script tags and its descriptions are directly extracted
41 // from `ttfautohint-scripts.h'
42 typedef struct Script_Names_
44 const char* tag;
45 const char* description;
46 } Script_Names;
48 #undef SCRIPT
49 #define SCRIPT(s, S, d) \
50 {#s, d},
52 const Script_Names script_names[] =
54 #include <ttfautohint-scripts.h>
55 {NULL, NULL}
59 Main_GUI::Main_GUI(bool horizontal_layout,
60 int range_min,
61 int range_max,
62 int limit,
63 bool gray,
64 bool gdi,
65 bool dw,
66 int increase,
67 const char* exceptions,
68 bool ignore,
69 bool wincomp,
70 bool pre,
71 bool composites,
72 bool no,
73 const char* fallback,
74 bool symb,
75 bool dh)
76 : hinting_range_min(range_min),
77 hinting_range_max(range_max),
78 hinting_limit(limit),
79 gray_strong_stem_width(gray),
80 gdi_cleartype_strong_stem_width(gdi),
81 dw_cleartype_strong_stem_width(dw),
82 increase_x_height(increase),
83 x_height_snapping_exceptions_string(exceptions),
84 ignore_restrictions(ignore),
85 windows_compatibility(wincomp),
86 pre_hinting(pre),
87 hint_composites(composites),
88 no_info(no),
89 symbol(symb),
90 dehint(dh)
92 int i;
93 int none_script_idx = 0;
95 // map fallback script tag to an index,
96 // replacing an invalid one with the default value
97 for (i = 0; script_names[i].tag; i++)
99 if (!strcmp("none", script_names[i].tag))
100 none_script_idx = i;
101 if (!strcmp(fallback, script_names[i].tag))
102 break;
104 fallback_script_idx = script_names[i].tag ? i : none_script_idx;
106 x_height_snapping_exceptions = NULL;
108 create_layout(horizontal_layout);
109 create_connections();
110 create_actions();
111 create_menus();
112 create_status_bar();
114 set_defaults();
115 read_settings();
117 setUnifiedTitleAndToolBarOnMac(true);
119 // XXX register translations somewhere and loop over them
120 if (QLocale::system().name() == "en_US")
121 locale = new QLocale;
122 else
123 locale = new QLocale(QLocale::C);
127 Main_GUI::~Main_GUI()
129 number_set_free(x_height_snapping_exceptions);
133 // overloading
135 void
136 Main_GUI::closeEvent(QCloseEvent* event)
138 write_settings();
139 event->accept();
143 void
144 Main_GUI::about()
146 QMessageBox::about(this,
147 tr("About TTFautohint"),
148 tr("<p>This is <b>TTFautohint</b> version %1<br>"
149 " Copyright %2 2011-2013<br>"
150 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
152 "<p><b>TTFautohint</b> adds new auto-generated hints"
153 " to a TrueType font or TrueType collection.</p>"
155 "<p>License:"
156 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
157 " License (FTL)</a> or"
158 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
159 " GPLv2</a></p>")
160 .arg(VERSION)
161 .arg(QChar(0xA9)));
165 void
166 Main_GUI::browse_input()
168 // XXX remember last directory
169 QString file = QFileDialog::getOpenFileName(
170 this,
171 tr("Open Input File"),
172 QDir::homePath(),
173 "");
174 if (!file.isEmpty())
175 input_line->setText(QDir::toNativeSeparators(file));
179 void
180 Main_GUI::browse_output()
182 // XXX remember last directory
183 QString file = QFileDialog::getSaveFileName(
184 this,
185 tr("Open Output File"),
186 QDir::homePath(),
187 "");
189 if (!file.isEmpty())
190 output_line->setText(QDir::toNativeSeparators(file));
194 void
195 Main_GUI::check_min()
197 int min = min_box->value();
198 int max = max_box->value();
199 int limit = limit_box->value();
200 if (min > max)
201 max_box->setValue(min);
202 if (min > limit)
203 limit_box->setValue(min);
207 void
208 Main_GUI::check_max()
210 int min = min_box->value();
211 int max = max_box->value();
212 int limit = limit_box->value();
213 if (max < min)
214 min_box->setValue(max);
215 if (max > limit)
216 limit_box->setValue(max);
220 void
221 Main_GUI::check_limit()
223 int min = min_box->value();
224 int max = max_box->value();
225 int limit = limit_box->value();
226 if (limit < max)
227 max_box->setValue(limit);
228 if (limit < min)
229 min_box->setValue(limit);
233 void
234 Main_GUI::check_dehint()
236 if (dehint_box->isChecked())
238 min_label->setEnabled(false);
239 min_box->setEnabled(false);
241 max_label->setEnabled(false);
242 max_box->setEnabled(false);
244 fallback_label->setEnabled(false);
245 fallback_box->setEnabled(false);
247 no_limit_box->setEnabled(false);
248 limit_label->setEnabled(false);
249 limit_box->setEnabled(false);
251 no_increase_box->setEnabled(false);
252 increase_label->setEnabled(false);
253 increase_box->setEnabled(false);
255 snapping_label->setEnabled(false);
256 snapping_line->setEnabled(false);
258 wincomp_box->setEnabled(false);
259 pre_box->setEnabled(false);
260 hint_box->setEnabled(false);
261 symbol_box->setEnabled(false);
263 stem_label->setEnabled(false);
264 gray_box->setEnabled(false);
265 gdi_box->setEnabled(false);
266 dw_box->setEnabled(false);
268 else
270 min_label->setEnabled(true);
271 min_box->setEnabled(true);
273 max_label->setEnabled(true);
274 max_box->setEnabled(true);
276 fallback_label->setEnabled(true);
277 fallback_box->setEnabled(true);
279 no_limit_box->setEnabled(true);
280 check_no_limit();
282 no_increase_box->setEnabled(true);
283 check_no_increase();
285 snapping_label->setEnabled(true);
286 snapping_line->setEnabled(true);
288 wincomp_box->setEnabled(true);
289 pre_box->setEnabled(true);
290 hint_box->setEnabled(true);
291 symbol_box->setEnabled(true);
293 stem_label->setEnabled(true);
294 gray_box->setEnabled(true);
295 gdi_box->setEnabled(true);
296 dw_box->setEnabled(true);
301 void
302 Main_GUI::check_no_limit()
304 if (no_limit_box->isChecked())
306 limit_label->setEnabled(false);
307 limit_box->setEnabled(false);
309 else
311 limit_label->setEnabled(true);
312 limit_box->setEnabled(true);
317 void
318 Main_GUI::check_no_increase()
320 if (no_increase_box->isChecked())
322 increase_label->setEnabled(false);
323 increase_box->setEnabled(false);
325 else
327 increase_label->setEnabled(true);
328 increase_box->setEnabled(true);
333 void
334 Main_GUI::check_run()
336 if (input_line->text().isEmpty() || output_line->text().isEmpty())
337 run_button->setEnabled(false);
338 else
339 run_button->setEnabled(true);
343 void
344 Main_GUI::absolute_input()
346 QString input_name = QDir::fromNativeSeparators(input_line->text());
347 if (!input_name.isEmpty()
348 && QDir::isRelativePath(input_name))
350 QDir cur_path(QDir::currentPath() + "/" + input_name);
351 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
356 void
357 Main_GUI::absolute_output()
359 QString output_name = QDir::fromNativeSeparators(output_line->text());
360 if (!output_name.isEmpty()
361 && QDir::isRelativePath(output_name))
363 QDir cur_path(QDir::currentPath() + "/" + output_name);
364 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
369 void
370 Main_GUI::check_number_set()
372 QString text = snapping_line->text();
373 QString qs;
375 // construct ASCII string from arbitrary Unicode data;
376 // the idea is to accept, say, CJK fullwidth digits also
377 for (int i = 0; i < text.size(); i++)
379 QChar c = text.at(i);
381 int digit = c.digitValue();
382 if (digit >= 0)
383 qs += QString::number(digit);
384 else if (c.isSpace())
385 qs += ' ';
386 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
387 // to the `-' key in some Japanese input methods
388 else if (c.category() == QChar::Punctuation_Dash
389 || c == QChar(0x30FC))
390 qs += '-';
391 // various Unicode COMMA characters,
392 // including representation forms
393 else if (c == QChar(',')
394 || c == QChar(0x055D)
395 || c == QChar(0x060C)
396 || c == QChar(0x07F8)
397 || c == QChar(0x1363)
398 || c == QChar(0x1802)
399 || c == QChar(0x1808)
400 || c == QChar(0x3001)
401 || c == QChar(0xA4FE)
402 || c == QChar(0xA60D)
403 || c == QChar(0xA6F5)
404 || c == QChar(0xFE10)
405 || c == QChar(0xFE11)
406 || c == QChar(0xFE50)
407 || c == QChar(0xFE51)
408 || c == QChar(0xFF0C)
409 || c == QChar(0xFF64))
410 qs += ',';
411 else
412 qs += c; // we do error handling below
415 if (x_height_snapping_exceptions)
416 number_set_free(x_height_snapping_exceptions);
418 QByteArray str = qs.toLocal8Bit();
419 const char* s = number_set_parse(str.constData(),
420 &x_height_snapping_exceptions,
421 6, 0x7FFF);
422 if (s && *s)
424 statusBar()->setStyleSheet("color: red;");
425 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
426 statusBar()->showMessage(
427 tr("allocation error"));
428 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
429 statusBar()->showMessage(
430 tr("invalid character (use digits, dashes, commas, and spaces)"));
431 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
432 statusBar()->showMessage(
433 tr("overflow"));
434 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
435 statusBar()->showMessage(
436 tr("invalid range (minimum is 6, maximum is 32767)"));
437 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
438 statusBar()->showMessage(
439 tr("overlapping ranges"));
440 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
441 statusBar()->showMessage(
442 tr("values und ranges must be specified in ascending order"));
444 snapping_line->setText(qs);
445 snapping_line->setFocus(Qt::OtherFocusReason);
446 snapping_line->setCursorPosition(s - str.constData());
448 x_height_snapping_exceptions = NULL;
450 else
452 // normalize if there is no error
453 char* new_str = number_set_show(x_height_snapping_exceptions,
454 6, 0x7FFF);
455 snapping_line->setText(new_str);
456 free(new_str);
461 void
462 Main_GUI::clear_status_bar()
464 statusBar()->clearMessage();
465 statusBar()->setStyleSheet("");
470 Main_GUI::check_filenames(const QString& input_name,
471 const QString& output_name)
473 if (!QFile::exists(input_name))
475 QMessageBox::warning(
476 this,
477 "TTFautohint",
478 tr("The file %1 cannot be found.")
479 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
480 QMessageBox::Ok,
481 QMessageBox::Ok);
482 return 0;
485 if (input_name == output_name)
487 QMessageBox::warning(
488 this,
489 "TTFautohint",
490 tr("Input and output file names must be different."),
491 QMessageBox::Ok,
492 QMessageBox::Ok);
493 return 0;
496 if (QFile::exists(output_name))
498 int ret = QMessageBox::warning(
499 this,
500 "TTFautohint",
501 tr("The file %1 already exists.\n"
502 "Overwrite?")
503 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
504 QMessageBox::Yes | QMessageBox::No,
505 QMessageBox::No);
506 if (ret == QMessageBox::No)
507 return 0;
510 return 1;
515 Main_GUI::open_files(const QString& input_name,
516 FILE** in,
517 const QString& output_name,
518 FILE** out)
520 const int buf_len = 1024;
521 char buf[buf_len];
523 *in = fopen(qPrintable(input_name), "rb");
524 if (!*in)
526 strerror_r(errno, buf, buf_len);
527 QMessageBox::warning(
528 this,
529 "TTFautohint",
530 tr("The following error occurred while opening input file %1:\n")
531 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
532 + QString::fromLocal8Bit(buf),
533 QMessageBox::Ok,
534 QMessageBox::Ok);
535 return 0;
538 *out = fopen(qPrintable(output_name), "wb");
539 if (!*out)
541 strerror_r(errno, buf, buf_len);
542 QMessageBox::warning(
543 this,
544 "TTFautohint",
545 tr("The following error occurred while opening output file %1:\n")
546 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
547 + QString::fromLocal8Bit(buf),
548 QMessageBox::Ok,
549 QMessageBox::Ok);
550 return 0;
553 return 1;
557 extern "C" {
559 struct GUI_Progress_Data
561 long last_sfnt;
562 bool begin;
563 QProgressDialog* dialog;
568 gui_progress(long curr_idx,
569 long num_glyphs,
570 long curr_sfnt,
571 long num_sfnts,
572 void* user)
574 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
576 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
578 data->dialog->setLabelText(QCoreApplication::translate(
579 "GuiProgress",
580 "Auto-hinting subfont %1 of %2"
581 " with %3 glyphs...")
582 .arg(curr_sfnt + 1)
583 .arg(num_sfnts)
584 .arg(num_glyphs));
586 if (curr_sfnt + 1 == num_sfnts)
588 data->dialog->setAutoReset(true);
589 data->dialog->setAutoClose(true);
591 else
593 data->dialog->setAutoReset(false);
594 data->dialog->setAutoClose(false);
597 data->last_sfnt = curr_sfnt;
598 data->begin = true;
601 if (data->begin)
603 if (num_sfnts == 1)
604 data->dialog->setLabelText(QCoreApplication::translate(
605 "GuiProgress",
606 "Auto-hinting %1 glyphs...")
607 .arg(num_glyphs));
608 data->dialog->setMaximum(num_glyphs - 1);
610 data->begin = false;
613 data->dialog->setValue(curr_idx);
615 if (data->dialog->wasCanceled())
616 return 1;
618 return 0;
621 } // extern "C"
624 // return value 1 indicates a retry
627 Main_GUI::handle_error(TA_Error error,
628 const unsigned char* error_string,
629 QString output_name)
631 int ret = 0;
633 if (error == TA_Err_Canceled)
635 else if (error == TA_Err_Invalid_FreeType_Version)
636 QMessageBox::critical(
637 this,
638 "TTFautohint",
639 tr("FreeType version 2.4.5 or higher is needed.\n"
640 "Are you perhaps using a wrong FreeType DLL?"),
641 QMessageBox::Ok,
642 QMessageBox::Ok);
643 else if (error == TA_Err_Invalid_Font_Type)
644 QMessageBox::warning(
645 this,
646 "TTFautohint",
647 tr("This font is not a valid font"
648 " in SFNT format with TrueType outlines.\n"
649 "In particular, CFF outlines are not supported."),
650 QMessageBox::Ok,
651 QMessageBox::Ok);
652 else if (error == TA_Err_Already_Processed)
653 QMessageBox::warning(
654 this,
655 "TTFautohint",
656 tr("This font has already been processed by TTFautohint."),
657 QMessageBox::Ok,
658 QMessageBox::Ok);
659 else if (error == TA_Err_Missing_Legal_Permission)
661 int yesno = QMessageBox::warning(
662 this,
663 "TTFautohint",
664 tr("Bit 1 in the %1 field of the %2 table is set:"
665 " This font must not be modified"
666 " without permission of the legal owner.\n"
667 "Do you have such a permission?")
668 .arg(QUOTE_STRING_LITERAL("fsType"))
669 .arg(QUOTE_STRING_LITERAL("OS/2")),
670 QMessageBox::Yes | QMessageBox::No,
671 QMessageBox::No);
672 if (yesno == QMessageBox::Yes)
674 ignore_restrictions = true;
675 ret = 1;
678 else if (error == TA_Err_Missing_Unicode_CMap)
679 QMessageBox::warning(
680 this,
681 "TTFautohint",
682 tr("No Unicode character map."),
683 QMessageBox::Ok,
684 QMessageBox::Ok);
685 else if (error == TA_Err_Missing_Symbol_CMap)
686 QMessageBox::warning(
687 this,
688 "TTFautohint",
689 tr("No symbol character map."),
690 QMessageBox::Ok,
691 QMessageBox::Ok);
692 else if (error == TA_Err_Missing_Glyph)
693 QMessageBox::warning(
694 this,
695 "TTFautohint",
696 tr("No glyph for the key character"
697 " to derive standard stem width and height.\n"
698 "For the latin script, this key character is %1 (U+006F).\n"
699 "\n"
700 "Set the %2 checkbox if you want to circumvent this test.")
701 .arg(QUOTE_STRING_LITERAL("o"))
702 .arg(QUOTE_STRING_LITERAL("symbol")),
703 QMessageBox::Ok,
704 QMessageBox::Ok);
705 else
706 QMessageBox::warning(
707 this,
708 "TTFautohint",
709 tr("Error code 0x%1 while autohinting font:\n")
710 .arg(error, 2, 16, QLatin1Char('0'))
711 + QString::fromLocal8Bit((const char*)error_string),
712 QMessageBox::Ok,
713 QMessageBox::Ok);
715 if (QFile::exists(output_name) && remove(qPrintable(output_name)))
717 const int buf_len = 1024;
718 char buf[buf_len];
720 strerror_r(errno, buf, buf_len);
721 QMessageBox::warning(
722 this,
723 "TTFautohint",
724 tr("The following error occurred while removing output file %1:\n")
725 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
726 + QString::fromLocal8Bit(buf),
727 QMessageBox::Ok,
728 QMessageBox::Ok);
731 return ret;
735 void
736 Main_GUI::run()
738 statusBar()->clearMessage();
740 QString input_name = QDir::fromNativeSeparators(input_line->text());
741 QString output_name = QDir::fromNativeSeparators(output_line->text());
742 if (!check_filenames(input_name, output_name))
743 return;
745 // we need C file descriptors for communication with TTF_autohint
746 FILE* input;
747 FILE* output;
749 again:
750 if (!open_files(input_name, &input, output_name, &output))
751 return;
753 QProgressDialog dialog;
754 dialog.setCancelButtonText(tr("Cancel"));
755 dialog.setMinimumDuration(1000);
756 dialog.setWindowModality(Qt::WindowModal);
758 const unsigned char* error_string;
759 TA_Info_Func info_func = info;
760 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
761 Info_Data info_data;
763 info_data.data = NULL; // must be deallocated after use
764 info_data.data_wide = NULL; // must be deallocated after use
765 info_data.data_len = 0;
766 info_data.data_wide_len = 0;
768 info_data.hinting_range_min = min_box->value();
769 info_data.hinting_range_max = max_box->value();
770 info_data.hinting_limit = no_limit_box->isChecked()
772 : limit_box->value();
774 info_data.gray_strong_stem_width = gray_box->isChecked();
775 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
776 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
778 info_data.increase_x_height = no_increase_box->isChecked()
780 : increase_box->value();
781 info_data.x_height_snapping_exceptions = x_height_snapping_exceptions;
783 info_data.windows_compatibility = wincomp_box->isChecked();
784 info_data.pre_hinting = pre_box->isChecked();
785 info_data.hint_composites = hint_box->isChecked();
786 info_data.symbol = symbol_box->isChecked();
787 info_data.dehint = dehint_box->isChecked();
789 strncpy(info_data.fallback_script,
790 script_names[fallback_box->currentIndex()].tag,
791 sizeof (info_data.fallback_script));
793 if (info_box->isChecked())
795 int ret = build_version_string(&info_data);
796 if (ret == 1)
797 QMessageBox::information(
798 this,
799 "TTFautohint",
800 tr("Can't allocate memory for <b>TTFautohint</b> options string"
801 " in <i>name</i> table."),
802 QMessageBox::Ok,
803 QMessageBox::Ok);
804 else if (ret == 2)
805 QMessageBox::information(
806 this,
807 "TTFautohint",
808 tr("<b>TTFautohint</b> options string"
809 " in <i>name</i> table too long."),
810 QMessageBox::Ok,
811 QMessageBox::Ok);
813 else
814 info_func = NULL;
816 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
818 TA_Error error =
819 TTF_autohint("in-file, out-file,"
820 "hinting-range-min, hinting-range-max,"
821 "hinting-limit,"
822 "gray-strong-stem-width,"
823 "gdi-cleartype-strong-stem-width,"
824 "dw-cleartype-strong-stem-width,"
825 "error-string,"
826 "progress-callback, progress-callback-data,"
827 "info-callback, info-callback-data,"
828 "ignore-restrictions,"
829 "windows-compatibility,"
830 "pre-hinting,"
831 "hint-composites,"
832 "increase-x-height,"
833 "x-height-snapping-exceptions,"
834 "fallback-script, symbol,"
835 "dehint",
836 input, output,
837 info_data.hinting_range_min, info_data.hinting_range_max,
838 info_data.hinting_limit,
839 info_data.gray_strong_stem_width,
840 info_data.gdi_cleartype_strong_stem_width,
841 info_data.dw_cleartype_strong_stem_width,
842 &error_string,
843 gui_progress, &gui_progress_data,
844 info_func, &info_data,
845 ignore_restrictions,
846 info_data.windows_compatibility,
847 info_data.pre_hinting,
848 info_data.hint_composites,
849 info_data.increase_x_height,
850 snapping_string.constData(),
851 info_data.fallback_script, info_data.symbol,
852 info_data.dehint);
854 if (info_box->isChecked())
856 free(info_data.data);
857 free(info_data.data_wide);
860 fclose(input);
861 fclose(output);
863 if (error)
865 if (handle_error(error, error_string, output_name))
866 goto again;
868 else
869 statusBar()->showMessage(tr("Auto-hinting finished."));
873 // XXX distances are specified in pixels,
874 // making the layout dependent on the output device resolution
875 void
876 Main_GUI::create_layout(bool horizontal_layout)
879 // file stuff
881 QCompleter* completer = new QCompleter(this);
882 QFileSystemModel* model = new QFileSystemModel(completer);
883 model->setRootPath(QDir::rootPath());
884 completer->setModel(model);
886 input_label = new QLabel(tr("&Input File:"));
887 input_line = new Drag_Drop_Line_Edit;
888 input_button = new QPushButton(tr("Browse..."));
889 input_label->setBuddy(input_line);
890 // enforce rich text to get nice word wrapping
891 input_label->setToolTip(
892 tr("<b></b>The input file, either a TrueType font (TTF),"
893 " TrueType collection (TTC), or a TrueType-based OpenType font."));
894 input_line->setCompleter(completer);
896 output_label = new QLabel(tr("&Output File:"));
897 output_line = new Drag_Drop_Line_Edit;
898 output_button = new QPushButton(tr("Browse..."));
899 output_label->setBuddy(output_line);
900 output_label->setToolTip(
901 tr("<b></b>The output file, which will be essentially identical"
902 " to the input font but will contain new, generated hints."));
903 output_line->setCompleter(completer);
906 // minmax controls
908 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
909 min_box = new QSpinBox;
910 min_label->setBuddy(min_box);
911 min_label->setToolTip(
912 tr("The minimum PPEM value of the range for which"
913 " <b>TTFautohint</b> computes <i>hint sets</i>."
914 " A hint set for a given PPEM value hints this size optimally."
915 " The larger the range, the more hint sets are considered,"
916 " usually increasing the size of the bytecode.<br>"
917 "Note that changing this range doesn't influence"
918 " the <i>gasp</i> table:"
919 " Hinting is enabled for all sizes."));
920 min_box->setKeyboardTracking(false);
921 min_box->setRange(2, 10000);
923 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
924 max_box = new QSpinBox;
925 max_label->setBuddy(max_box);
926 max_label->setToolTip(
927 tr("The maximum PPEM value of the range for which"
928 " <b>TTFautohint</b> computes <i>hint sets</i>."
929 " A hint set for a given PPEM value hints this size optimally."
930 " The larger the range, the more hint sets are considered,"
931 " usually increasing the size of the bytecode.<br>"
932 "Note that changing this range doesn't influence"
933 " the <i>gasp</i> table:"
934 " Hinting is enabled for all sizes."));
935 max_box->setKeyboardTracking(false);
936 max_box->setRange(2, 10000);
939 // hinting and fallback controls
941 fallback_label = new QLabel(tr("Fallback &Script:"));
942 fallback_box = new QComboBox;
943 fallback_label->setBuddy(fallback_box);
944 fallback_label->setToolTip(
945 tr("This sets the fallback script module for glyphs"
946 " that <b>TTFautohint</b> can't map to a script automatically."));
947 for (int i = 0; script_names[i].tag; i++)
949 // XXX: how to provide translations?
950 fallback_box->insertItem(i,
951 QString("%1 (%2)")
952 .arg(script_names[i].tag)
953 .arg(script_names[i].description));
957 // hinting limit
959 limit_label = new QLabel(tr("Hinting &Limit:"));
960 limit_box = new QSpinBox;
961 limit_label->setBuddy(limit_box);
962 limit_label->setToolTip(
963 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
964 " sizes larger than this PPEM value are not hinted"
965 " (regardless of the values in the <i>gasp</i> table)."));
966 limit_box->setKeyboardTracking(false);
967 limit_box->setRange(2, 10000);
969 no_limit_box = new QCheckBox(tr("No Hinting Limit"), this);
970 no_limit_box->setToolTip(
971 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
972 " to the bytecode."));
975 // x height increase limit
977 increase_label = new QLabel(tr("x Height In&crease Limit:"));
978 increase_box = new QSpinBox;
979 increase_label->setBuddy(increase_box);
980 increase_label->setToolTip(
981 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
982 " where <i>n</i> is the value selected by this spin box,"
983 " round up the font's x&nbsp;height much more often than normally.<br>"
984 "Use this if holes in letters like <i>e</i> get filled,"
985 " for example."));
986 increase_box->setKeyboardTracking(false);
987 increase_box->setRange(6, 10000);
989 no_increase_box = new QCheckBox(tr("No x Height Increase"), this);
990 no_increase_box->setToolTip(
991 tr("If switched on,"
992 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
995 // x height snapping exceptions
997 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
998 snapping_line = new Tooltip_Line_Edit;
999 snapping_label->setBuddy(snapping_line);
1000 snapping_label->setToolTip(
1001 tr("<p>A list of comma separated PPEM values or value ranges"
1002 " at which no x&nbsp;height snapping shall be applied"
1003 " (x&nbsp;height snapping usually slightly increases"
1004 " the size of all glyphs).</p>"
1006 "Examples:<br>"
1007 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1008 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1009 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1010 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1013 // flags
1015 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1016 wincomp_box->setToolTip(
1017 tr("If switched on, add two artificial blue zones positioned at the"
1018 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1019 " (from the font's <i>OS/2</i> table)."
1020 " This option, usually in combination"
1021 " with value <tt>-</tt> (a single dash)"
1022 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1023 " should be used if those two <i>OS/2</i> values are tight,"
1024 " and you are experiencing clipping during rendering."));
1026 pre_box = new QCheckBox(tr("Pr&e-hinting"), this);
1027 pre_box->setToolTip(
1028 tr("If switched on, the original bytecode of the input font"
1029 " gets applied before <b>TTFautohint</b> starts processing"
1030 " the outlines of the glyphs."));
1032 hint_box = new QCheckBox(tr("Hint Co&mposites")
1033 + " ", this); // make label wider
1034 hint_box->setToolTip(
1035 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1036 " as a whole, including subglyphs."
1037 " Otherwise, glyph components get hinted separately.<br>"
1038 "Deactivating this flag reduces the bytecode size enormously,"
1039 " however, it might yield worse results."));
1041 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1042 symbol_box->setToolTip(
1043 tr("If switched on, <b>TTFautohint</b> uses default values"
1044 " for standard stem width and height"
1045 " instead of deriving these values from the input font.<br>"
1046 "Use this for fonts that don't contain glyphs"
1047 " of a (supported) script."));
1049 dehint_box = new QCheckBox(tr("&Dehint"), this);
1050 dehint_box->setToolTip(
1051 tr("<b></b>If set, remove all hints from the font."));
1053 info_box = new QCheckBox(tr("Add ttf&autohint Info"), this);
1054 info_box->setToolTip(
1055 tr("If switched on, information about <b>TTFautohint</b>"
1056 " and its calling parameters are added to the version string(s)"
1057 " (name ID&nbsp;5) in the <i>name</i> table."));
1060 // stem width and positioning
1062 stem_label = new QLabel(tr("Strong Stem &Width and Positioning:"));
1063 stem_label->setToolTip(
1064 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1065 " that can be selected for various hinting modes."
1067 "<p><i>strong</i> (checkbox set):"
1068 " Position horizontal stems and snap stem widths"
1069 " to integer pixel values. While making the output look crisper,"
1070 " outlines become more distorted.</p>"
1072 "<p><i>smooth</i> (checkbox not set):"
1073 " Use discrete values for horizontal stems and stem widths."
1074 " This only slightly increases the contrast"
1075 " but avoids larger outline distortion.</p>"));
1077 gray_box = new QCheckBox(tr("Grayscale"), this);
1078 gray_box->setToolTip(
1079 tr("<b></b>Grayscale rendering, no ClearType activated."));
1080 stem_label->setBuddy(gray_box);
1082 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1083 gdi_box->setToolTip(
1084 tr("GDI ClearType rendering,"
1085 " introduced in 2000 for Windows XP.<br>"
1086 "The rasterizer version (as returned by the"
1087 " GETINFO bytecode instruction) is in the range"
1088 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1089 "Along the vertical axis, this mode behaves like B/W rendering."));
1091 dw_box = new QCheckBox(tr("DW ClearType"), this);
1092 dw_box->setToolTip(
1093 tr("DirectWrite ClearType rendering,"
1094 " introduced in 2008 for Windows Vista.<br>"
1095 "The rasterizer version (as returned by the"
1096 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1097 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1098 "Smooth rendering along the vertical axis."));
1101 // running
1103 run_button = new QPushButton(" "
1104 + tr("&Run")
1105 + " "); // make label wider
1107 if (horizontal_layout)
1108 create_horizontal_layout();
1109 else
1110 create_vertical_layout();
1114 // XXX distances are specified in pixels,
1115 // making the layout dependent on the output device resolution
1116 void
1117 Main_GUI::create_vertical_layout()
1119 // top area
1120 QGridLayout* file_layout = new QGridLayout;
1122 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1123 file_layout->addWidget(input_line, 0, 1);
1124 file_layout->addWidget(input_button, 0, 2);
1126 file_layout->setRowStretch(1, 1);
1128 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1129 file_layout->addWidget(output_line, 2, 1);
1130 file_layout->addWidget(output_button, 2, 2);
1133 // the whole gui
1135 QGridLayout* gui_layout = new QGridLayout;
1136 QFrame* hline = new QFrame;
1137 hline->setFrameShape(QFrame::HLine);
1138 int row = 0; // this counter simplifies inserting new items
1140 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1141 gui_layout->setRowStretch(row++, 1);
1143 gui_layout->addLayout(file_layout, row, 0, row, -1);
1144 gui_layout->setRowStretch(row++, 1);
1146 gui_layout->addWidget(hline, row, 0, row, -1);
1147 gui_layout->setRowStretch(row++, 1);
1149 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1150 gui_layout->setRowStretch(row++, 1);
1152 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1153 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1154 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1155 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1157 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1158 gui_layout->setRowStretch(row++, 1);
1160 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1161 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1163 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1164 gui_layout->setRowStretch(row++, 1);
1166 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1167 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1168 gui_layout->addWidget(no_limit_box, row++, 1);
1170 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1171 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1172 gui_layout->addWidget(no_increase_box, row++, 1);
1174 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1175 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1177 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1178 gui_layout->setRowStretch(row++, 1);
1180 gui_layout->addWidget(wincomp_box, row++, 1);
1181 gui_layout->addWidget(pre_box, row++, 1);
1182 gui_layout->addWidget(hint_box, row++, 1);
1183 gui_layout->addWidget(symbol_box, row++, 1);
1184 gui_layout->addWidget(dehint_box, row++, 1);
1185 gui_layout->addWidget(info_box, row++, 1);
1187 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1188 gui_layout->setRowStretch(row++, 1);
1190 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1191 gui_layout->addWidget(gray_box, row++, 1);
1192 gui_layout->addWidget(gdi_box, row++, 1);
1193 gui_layout->addWidget(dw_box, row++, 1);
1195 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1196 gui_layout->setRowStretch(row++, 1);
1198 gui_layout->addWidget(run_button, row++, 1, Qt::AlignRight);
1200 // create dummy widget to register layout
1201 QWidget* main_widget = new QWidget;
1202 main_widget->setLayout(gui_layout);
1203 setCentralWidget(main_widget);
1204 setWindowTitle("TTFautohint");
1208 // XXX distances are specified in pixels,
1209 // making the layout dependent on the output device resolution
1210 void
1211 Main_GUI::create_horizontal_layout()
1213 // top area
1214 QGridLayout* file_layout = new QGridLayout;
1216 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1217 file_layout->addWidget(input_line, 0, 1);
1218 file_layout->addWidget(input_button, 0, 2);
1220 file_layout->setRowStretch(1, 1);
1222 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1223 file_layout->addWidget(output_line, 2, 1);
1224 file_layout->addWidget(output_button, 2, 2);
1226 QGridLayout* gui_layout = new QGridLayout;
1227 QFrame* hline = new QFrame;
1228 hline->setFrameShape(QFrame::HLine);
1229 int row = 0; // this counter simplifies inserting new items
1231 // margin
1232 gui_layout->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1233 gui_layout->setColumnStretch(0, 1);
1235 // left
1236 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1237 gui_layout->setRowStretch(row++, 1);
1239 gui_layout->addLayout(file_layout, row, 0, row, -1);
1240 gui_layout->setRowStretch(row++, 1);
1242 gui_layout->addWidget(hline, row, 0, row, -1);
1243 gui_layout->setRowStretch(row++, 1);
1245 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1246 gui_layout->setRowStretch(row++, 1);
1248 gui_layout->addWidget(min_label, row, 1, Qt::AlignRight);
1249 gui_layout->addWidget(min_box, row++, 2, Qt::AlignLeft);
1250 gui_layout->addWidget(max_label, row, 1, Qt::AlignRight);
1251 gui_layout->addWidget(max_box, row++, 2, Qt::AlignLeft);
1253 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1254 gui_layout->setRowStretch(row++, 1);
1256 gui_layout->addWidget(fallback_label, row, 1, Qt::AlignRight);
1257 gui_layout->addWidget(fallback_box, row++, 2, Qt::AlignLeft);
1259 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1260 gui_layout->setRowStretch(row++, 1);
1262 gui_layout->addWidget(limit_label, row, 1, Qt::AlignRight);
1263 gui_layout->addWidget(limit_box, row++, 2, Qt::AlignLeft);
1264 gui_layout->addWidget(no_limit_box, row++, 2);
1266 gui_layout->addWidget(increase_label, row, 1, Qt::AlignRight);
1267 gui_layout->addWidget(increase_box, row++, 2, Qt::AlignLeft);
1268 gui_layout->addWidget(no_increase_box, row++, 2);
1270 gui_layout->addWidget(snapping_label, row, 1, Qt::AlignRight);
1271 gui_layout->addWidget(snapping_line, row++, 2, Qt::AlignLeft);
1273 // column separator
1274 gui_layout->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1275 gui_layout->setColumnStretch(3, 1);
1277 // right
1278 row = 4;
1279 gui_layout->addWidget(wincomp_box, row++, 4);
1280 gui_layout->addWidget(pre_box, row++, 4);
1281 gui_layout->addWidget(hint_box, row++, 4);
1282 gui_layout->addWidget(symbol_box, row++, 4);
1283 gui_layout->addWidget(dehint_box, row++, 4);
1284 gui_layout->addWidget(info_box, row++, 4);
1286 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1287 gui_layout->setRowStretch(row++, 1);
1289 gui_layout->addWidget(stem_label, row++, 4);
1291 QGridLayout* stem_layout = new QGridLayout;
1292 stem_layout->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1293 stem_layout->addWidget(gray_box, 0, 1);
1294 stem_layout->addWidget(gdi_box, 1, 1);
1295 stem_layout->addWidget(dw_box, 2, 1);
1297 gui_layout->addLayout(stem_layout, row, 4, 3, 1);
1298 row += 3;
1300 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1301 gui_layout->setRowStretch(row++, 1);
1303 gui_layout->addWidget(run_button, row++, 4, Qt::AlignRight);
1305 // margin
1306 gui_layout->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1307 gui_layout->setColumnStretch(5, 1);
1309 // create dummy widget to register layout
1310 QWidget* main_widget = new QWidget;
1311 main_widget->setLayout(gui_layout);
1312 setCentralWidget(main_widget);
1313 setWindowTitle("TTFautohint");
1317 void
1318 Main_GUI::create_connections()
1320 connect(input_button, SIGNAL(clicked()), this,
1321 SLOT(browse_input()));
1322 connect(output_button, SIGNAL(clicked()), this,
1323 SLOT(browse_output()));
1325 connect(input_line, SIGNAL(textChanged(QString)), this,
1326 SLOT(check_run()));
1327 connect(output_line, SIGNAL(textChanged(QString)), this,
1328 SLOT(check_run()));
1330 connect(input_line, SIGNAL(editingFinished()), this,
1331 SLOT(absolute_input()));
1332 connect(output_line, SIGNAL(editingFinished()), this,
1333 SLOT(absolute_output()));
1335 connect(min_box, SIGNAL(valueChanged(int)), this,
1336 SLOT(check_min()));
1337 connect(max_box, SIGNAL(valueChanged(int)), this,
1338 SLOT(check_max()));
1340 connect(limit_box, SIGNAL(valueChanged(int)), this,
1341 SLOT(check_limit()));
1342 connect(no_limit_box, SIGNAL(clicked()), this,
1343 SLOT(check_no_limit()));
1345 connect(no_increase_box, SIGNAL(clicked()), this,
1346 SLOT(check_no_increase()));
1348 connect(snapping_line, SIGNAL(editingFinished()), this,
1349 SLOT(check_number_set()));
1350 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1351 SLOT(clear_status_bar()));
1353 connect(dehint_box, SIGNAL(clicked()), this,
1354 SLOT(check_dehint()));
1356 connect(run_button, SIGNAL(clicked()), this,
1357 SLOT(run()));
1361 void
1362 Main_GUI::create_actions()
1364 exit_act = new QAction(tr("E&xit"), this);
1365 exit_act->setShortcuts(QKeySequence::Quit);
1366 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1368 about_act = new QAction(tr("&About"), this);
1369 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1371 about_Qt_act = new QAction(tr("About &Qt"), this);
1372 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1376 void
1377 Main_GUI::create_menus()
1379 file_menu = menuBar()->addMenu(tr("&File"));
1380 file_menu->addAction(exit_act);
1382 help_menu = menuBar()->addMenu(tr("&Help"));
1383 help_menu->addAction(about_act);
1384 help_menu->addAction(about_Qt_act);
1388 void
1389 Main_GUI::create_status_bar()
1391 statusBar()->showMessage("");
1395 void
1396 Main_GUI::set_defaults()
1398 min_box->setValue(hinting_range_min);
1399 max_box->setValue(hinting_range_max);
1401 fallback_box->setCurrentIndex(fallback_script_idx);
1403 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1404 // handle command line option `--hinting-limit=0'
1405 if (!hinting_limit)
1407 hinting_limit = max_box->value();
1408 no_limit_box->setChecked(true);
1411 increase_box->setValue(increase_x_height ? increase_x_height
1412 : TA_INCREASE_X_HEIGHT);
1413 // handle command line option `--increase-x-height=0'
1414 if (!increase_x_height)
1416 increase_x_height = TA_INCREASE_X_HEIGHT;
1417 no_increase_box->setChecked(true);
1420 snapping_line->setText(x_height_snapping_exceptions_string);
1422 if (windows_compatibility)
1423 wincomp_box->setChecked(true);
1424 if (pre_hinting)
1425 pre_box->setChecked(true);
1426 if (hint_composites)
1427 hint_box->setChecked(true);
1428 if (symbol)
1429 symbol_box->setChecked(true);
1430 if (dehint)
1431 dehint_box->setChecked(true);
1432 if (!no_info)
1433 info_box->setChecked(true);
1435 if (gray_strong_stem_width)
1436 gray_box->setChecked(true);
1437 if (gdi_cleartype_strong_stem_width)
1438 gdi_box->setChecked(true);
1439 if (dw_cleartype_strong_stem_width)
1440 dw_box->setChecked(true);
1442 run_button->setEnabled(false);
1444 check_min();
1445 check_max();
1446 check_limit();
1448 check_no_limit();
1449 check_no_increase();
1450 check_number_set();
1452 // do this last since it disables almost everything
1453 check_dehint();
1457 void
1458 Main_GUI::read_settings()
1460 QSettings settings;
1461 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1462 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1463 // resize(size);
1464 // move(pos);
1468 void
1469 Main_GUI::write_settings()
1471 QSettings settings;
1472 // settings.setValue("pos", pos());
1473 // settings.setValue("size", size());
1476 // end of maingui.cpp