Typo.
[ttfautohint.git] / frontend / maingui.cpp
blobaaac0ded4ca64bdd7b4ed1672fbdd892d42130f1
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(int range_min,
60 int range_max,
61 int limit,
62 bool gray,
63 bool gdi,
64 bool dw,
65 int increase,
66 const char* exceptions,
67 bool ignore,
68 bool wincomp,
69 bool pre,
70 bool composites,
71 bool no,
72 const char* fallback,
73 bool symb,
74 bool dh)
75 : hinting_range_min(range_min),
76 hinting_range_max(range_max),
77 hinting_limit(limit),
78 gray_strong_stem_width(gray),
79 gdi_cleartype_strong_stem_width(gdi),
80 dw_cleartype_strong_stem_width(dw),
81 increase_x_height(increase),
82 x_height_snapping_exceptions_string(exceptions),
83 ignore_restrictions(ignore),
84 windows_compatibility(wincomp),
85 pre_hinting(pre),
86 hint_composites(composites),
87 no_info(no),
88 symbol(symb),
89 dehint(dh)
91 int i;
92 int dflt_script_idx = 0;
94 // map fallback script tag to an index,
95 // replacing an invalid one with the default value
96 for (i = 0; script_names[i].tag; i++)
98 if (!strcmp("dflt", script_names[i].tag))
99 dflt_script_idx = i;
100 if (!strcmp(fallback, script_names[i].tag))
101 break;
103 fallback_script_idx = script_names[i].tag ? i : dflt_script_idx;
105 x_height_snapping_exceptions = NULL;
107 create_layout();
108 create_connections();
109 create_actions();
110 create_menus();
111 create_status_bar();
113 set_defaults();
114 read_settings();
116 setUnifiedTitleAndToolBarOnMac(true);
118 // XXX register translations somewhere and loop over them
119 if (QLocale::system().name() == "en_US")
120 locale = new QLocale;
121 else
122 locale = new QLocale(QLocale::C);
126 Main_GUI::~Main_GUI()
128 number_set_free(x_height_snapping_exceptions);
132 // overloading
134 void
135 Main_GUI::closeEvent(QCloseEvent* event)
137 write_settings();
138 event->accept();
142 void
143 Main_GUI::about()
145 QMessageBox::about(this,
146 tr("About TTFautohint"),
147 tr("<p>This is <b>TTFautohint</b> version %1<br>"
148 " Copyright %2 2011-2013<br>"
149 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
151 "<p><b>TTFautohint</b> adds new auto-generated hints"
152 " to a TrueType font or TrueType collection.</p>"
154 "<p>License:"
155 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
156 " License (FTL)</a> or"
157 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
158 " GPLv2</a></p>")
159 .arg(VERSION)
160 .arg(QChar(0xA9)));
164 void
165 Main_GUI::browse_input()
167 // XXX remember last directory
168 QString file = QFileDialog::getOpenFileName(
169 this,
170 tr("Open Input File"),
171 QDir::homePath(),
172 "");
173 if (!file.isEmpty())
174 input_line->setText(QDir::toNativeSeparators(file));
178 void
179 Main_GUI::browse_output()
181 // XXX remember last directory
182 QString file = QFileDialog::getSaveFileName(
183 this,
184 tr("Open Output File"),
185 QDir::homePath(),
186 "");
188 if (!file.isEmpty())
189 output_line->setText(QDir::toNativeSeparators(file));
193 void
194 Main_GUI::check_min()
196 int min = min_box->value();
197 int max = max_box->value();
198 int limit = limit_box->value();
199 if (min > max)
200 max_box->setValue(min);
201 if (min > limit)
202 limit_box->setValue(min);
206 void
207 Main_GUI::check_max()
209 int min = min_box->value();
210 int max = max_box->value();
211 int limit = limit_box->value();
212 if (max < min)
213 min_box->setValue(max);
214 if (max > limit)
215 limit_box->setValue(max);
219 void
220 Main_GUI::check_limit()
222 int min = min_box->value();
223 int max = max_box->value();
224 int limit = limit_box->value();
225 if (limit < max)
226 max_box->setValue(limit);
227 if (limit < min)
228 min_box->setValue(limit);
232 void
233 Main_GUI::check_dehint()
235 if (dehint_box->isChecked())
237 min_label->setEnabled(false);
238 min_box->setEnabled(false);
240 max_label->setEnabled(false);
241 max_box->setEnabled(false);
243 fallback_label->setEnabled(false);
244 fallback_box->setEnabled(false);
246 no_limit_box->setEnabled(false);
247 limit_label->setEnabled(false);
248 limit_box->setEnabled(false);
250 no_increase_box->setEnabled(false);
251 increase_label->setEnabled(false);
252 increase_box->setEnabled(false);
254 snapping_label->setEnabled(false);
255 snapping_line->setEnabled(false);
257 wincomp_box->setEnabled(false);
258 pre_box->setEnabled(false);
259 hint_box->setEnabled(false);
260 symbol_box->setEnabled(false);
262 stem_label->setEnabled(false);
263 gray_box->setEnabled(false);
264 gdi_box->setEnabled(false);
265 dw_box->setEnabled(false);
267 else
269 min_label->setEnabled(true);
270 min_box->setEnabled(true);
272 max_label->setEnabled(true);
273 max_box->setEnabled(true);
275 fallback_label->setEnabled(true);
276 fallback_box->setEnabled(true);
278 no_limit_box->setEnabled(true);
279 check_no_limit();
281 no_increase_box->setEnabled(true);
282 check_no_increase();
284 snapping_label->setEnabled(true);
285 snapping_line->setEnabled(true);
287 wincomp_box->setEnabled(true);
288 pre_box->setEnabled(true);
289 hint_box->setEnabled(true);
290 symbol_box->setEnabled(true);
292 stem_label->setEnabled(true);
293 gray_box->setEnabled(true);
294 gdi_box->setEnabled(true);
295 dw_box->setEnabled(true);
300 void
301 Main_GUI::check_no_limit()
303 if (no_limit_box->isChecked())
305 limit_label->setEnabled(false);
306 limit_box->setEnabled(false);
308 else
310 limit_label->setEnabled(true);
311 limit_box->setEnabled(true);
316 void
317 Main_GUI::check_no_increase()
319 if (no_increase_box->isChecked())
321 increase_label->setEnabled(false);
322 increase_box->setEnabled(false);
324 else
326 increase_label->setEnabled(true);
327 increase_box->setEnabled(true);
332 void
333 Main_GUI::check_run()
335 if (input_line->text().isEmpty() || output_line->text().isEmpty())
336 run_button->setEnabled(false);
337 else
338 run_button->setEnabled(true);
342 void
343 Main_GUI::absolute_input()
345 QString input_name = QDir::fromNativeSeparators(input_line->text());
346 if (!input_name.isEmpty()
347 && QDir::isRelativePath(input_name))
349 QDir cur_path(QDir::currentPath() + "/" + input_name);
350 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
355 void
356 Main_GUI::absolute_output()
358 QString output_name = QDir::fromNativeSeparators(output_line->text());
359 if (!output_name.isEmpty()
360 && QDir::isRelativePath(output_name))
362 QDir cur_path(QDir::currentPath() + "/" + output_name);
363 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
368 void
369 Main_GUI::check_number_set()
371 QString text = snapping_line->text();
372 QString qs;
374 // construct ASCII string from arbitrary Unicode data;
375 // the idea is to accept, say, CJK fullwidth digits also
376 for (int i = 0; i < text.size(); i++)
378 QChar c = text.at(i);
380 int digit = c.digitValue();
381 if (digit >= 0)
382 qs += QString::number(digit);
383 else if (c.isSpace())
384 qs += ' ';
385 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
386 // to the `-' key in some Japanese input methods
387 else if (c.category() == QChar::Punctuation_Dash
388 || c == QChar(0x30FC))
389 qs += '-';
390 // various Unicode COMMA characters,
391 // including representation forms
392 else if (c == QChar(',')
393 || c == QChar(0x055D)
394 || c == QChar(0x060C)
395 || c == QChar(0x07F8)
396 || c == QChar(0x1363)
397 || c == QChar(0x1802)
398 || c == QChar(0x1808)
399 || c == QChar(0x3001)
400 || c == QChar(0xA4FE)
401 || c == QChar(0xA60D)
402 || c == QChar(0xA6F5)
403 || c == QChar(0xFE10)
404 || c == QChar(0xFE11)
405 || c == QChar(0xFE50)
406 || c == QChar(0xFE51)
407 || c == QChar(0xFF0C)
408 || c == QChar(0xFF64))
409 qs += ',';
410 else
411 qs += c; // we do error handling below
414 if (x_height_snapping_exceptions)
415 number_set_free(x_height_snapping_exceptions);
417 QByteArray str = qs.toLocal8Bit();
418 const char* s = number_set_parse(str.constData(),
419 &x_height_snapping_exceptions,
420 6, 0x7FFF);
421 if (s && *s)
423 statusBar()->setStyleSheet("color: red;");
424 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
425 statusBar()->showMessage(
426 tr("allocation error"));
427 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
428 statusBar()->showMessage(
429 tr("invalid character (use digits, dashes, commas, and spaces)"));
430 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
431 statusBar()->showMessage(
432 tr("overflow"));
433 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
434 statusBar()->showMessage(
435 tr("invalid range (minimum is 6, maximum is 32767)"));
436 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
437 statusBar()->showMessage(
438 tr("overlapping ranges"));
439 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
440 statusBar()->showMessage(
441 tr("values und ranges must be specified in ascending order"));
443 snapping_line->setText(qs);
444 snapping_line->setFocus(Qt::OtherFocusReason);
445 snapping_line->setCursorPosition(s - str.constData());
447 x_height_snapping_exceptions = NULL;
449 else
451 // normalize if there is no error
452 char* new_str = number_set_show(x_height_snapping_exceptions,
453 6, 0x7FFF);
454 snapping_line->setText(new_str);
455 free(new_str);
460 void
461 Main_GUI::clear_status_bar()
463 statusBar()->clearMessage();
464 statusBar()->setStyleSheet("");
469 Main_GUI::check_filenames(const QString& input_name,
470 const QString& output_name)
472 if (!QFile::exists(input_name))
474 QMessageBox::warning(
475 this,
476 "TTFautohint",
477 tr("The file %1 cannot be found.")
478 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
479 QMessageBox::Ok,
480 QMessageBox::Ok);
481 return 0;
484 if (input_name == output_name)
486 QMessageBox::warning(
487 this,
488 "TTFautohint",
489 tr("Input and output file names must be different."),
490 QMessageBox::Ok,
491 QMessageBox::Ok);
492 return 0;
495 if (QFile::exists(output_name))
497 int ret = QMessageBox::warning(
498 this,
499 "TTFautohint",
500 tr("The file %1 already exists.\n"
501 "Overwrite?")
502 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
503 QMessageBox::Yes | QMessageBox::No,
504 QMessageBox::No);
505 if (ret == QMessageBox::No)
506 return 0;
509 return 1;
514 Main_GUI::open_files(const QString& input_name,
515 FILE** in,
516 const QString& output_name,
517 FILE** out)
519 const int buf_len = 1024;
520 char buf[buf_len];
522 *in = fopen(qPrintable(input_name), "rb");
523 if (!*in)
525 strerror_r(errno, buf, buf_len);
526 QMessageBox::warning(
527 this,
528 "TTFautohint",
529 tr("The following error occurred while opening input file %1:\n")
530 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
531 + QString::fromLocal8Bit(buf),
532 QMessageBox::Ok,
533 QMessageBox::Ok);
534 return 0;
537 *out = fopen(qPrintable(output_name), "wb");
538 if (!*out)
540 strerror_r(errno, buf, buf_len);
541 QMessageBox::warning(
542 this,
543 "TTFautohint",
544 tr("The following error occurred while opening output file %1:\n")
545 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
546 + QString::fromLocal8Bit(buf),
547 QMessageBox::Ok,
548 QMessageBox::Ok);
549 return 0;
552 return 1;
556 extern "C" {
558 struct GUI_Progress_Data
560 long last_sfnt;
561 bool begin;
562 QProgressDialog* dialog;
567 gui_progress(long curr_idx,
568 long num_glyphs,
569 long curr_sfnt,
570 long num_sfnts,
571 void* user)
573 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
575 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
577 data->dialog->setLabelText(QCoreApplication::translate(
578 "GuiProgress",
579 "Auto-hinting subfont %1 of %2"
580 " with %3 glyphs...")
581 .arg(curr_sfnt + 1)
582 .arg(num_sfnts)
583 .arg(num_glyphs));
585 if (curr_sfnt + 1 == num_sfnts)
587 data->dialog->setAutoReset(true);
588 data->dialog->setAutoClose(true);
590 else
592 data->dialog->setAutoReset(false);
593 data->dialog->setAutoClose(false);
596 data->last_sfnt = curr_sfnt;
597 data->begin = true;
600 if (data->begin)
602 if (num_sfnts == 1)
603 data->dialog->setLabelText(QCoreApplication::translate(
604 "GuiProgress",
605 "Auto-hinting %1 glyphs...")
606 .arg(num_glyphs));
607 data->dialog->setMaximum(num_glyphs - 1);
609 data->begin = false;
612 data->dialog->setValue(curr_idx);
614 if (data->dialog->wasCanceled())
615 return 1;
617 return 0;
620 } // extern "C"
623 // return value 1 indicates a retry
626 Main_GUI::handle_error(TA_Error error,
627 const unsigned char* error_string,
628 QString output_name)
630 int ret = 0;
632 if (error == TA_Err_Canceled)
634 else if (error == TA_Err_Invalid_FreeType_Version)
635 QMessageBox::critical(
636 this,
637 "TTFautohint",
638 tr("FreeType version 2.4.5 or higher is needed.\n"
639 "Are you perhaps using a wrong FreeType DLL?"),
640 QMessageBox::Ok,
641 QMessageBox::Ok);
642 else if (error == TA_Err_Invalid_Font_Type)
643 QMessageBox::warning(
644 this,
645 "TTFautohint",
646 tr("This font is not a valid font"
647 " in SFNT format with TrueType outlines.\n"
648 "In particular, CFF outlines are not supported."),
649 QMessageBox::Ok,
650 QMessageBox::Ok);
651 else if (error == TA_Err_Already_Processed)
652 QMessageBox::warning(
653 this,
654 "TTFautohint",
655 tr("This font has already been processed by TTFautohint."),
656 QMessageBox::Ok,
657 QMessageBox::Ok);
658 else if (error == TA_Err_Missing_Legal_Permission)
660 int yesno = QMessageBox::warning(
661 this,
662 "TTFautohint",
663 tr("Bit 1 in the %1 field of the %2 table is set:"
664 " This font must not be modified"
665 " without permission of the legal owner.\n"
666 "Do you have such a permission?")
667 .arg(QUOTE_STRING_LITERAL("fsType"))
668 .arg(QUOTE_STRING_LITERAL("OS/2")),
669 QMessageBox::Yes | QMessageBox::No,
670 QMessageBox::No);
671 if (yesno == QMessageBox::Yes)
673 ignore_restrictions = true;
674 ret = 1;
677 else if (error == TA_Err_Missing_Unicode_CMap)
678 QMessageBox::warning(
679 this,
680 "TTFautohint",
681 tr("No Unicode character map."),
682 QMessageBox::Ok,
683 QMessageBox::Ok);
684 else if (error == TA_Err_Missing_Symbol_CMap)
685 QMessageBox::warning(
686 this,
687 "TTFautohint",
688 tr("No symbol character map."),
689 QMessageBox::Ok,
690 QMessageBox::Ok);
691 else if (error == TA_Err_Missing_Glyph)
692 QMessageBox::warning(
693 this,
694 "TTFautohint",
695 tr("No glyph for the key character"
696 " to derive standard stem width and height.\n"
697 "For the latin script, this key character is %1 (U+006F).\n"
698 "\n"
699 "Set the %2 checkbox if you want to circumvent this test.")
700 .arg(QUOTE_STRING_LITERAL("o"))
701 .arg(QUOTE_STRING_LITERAL("symbol")),
702 QMessageBox::Ok,
703 QMessageBox::Ok);
704 else
705 QMessageBox::warning(
706 this,
707 "TTFautohint",
708 tr("Error code 0x%1 while autohinting font:\n")
709 .arg(error, 2, 16, QLatin1Char('0'))
710 + QString::fromLocal8Bit((const char*)error_string),
711 QMessageBox::Ok,
712 QMessageBox::Ok);
714 if (QFile::exists(output_name) && remove(qPrintable(output_name)))
716 const int buf_len = 1024;
717 char buf[buf_len];
719 strerror_r(errno, buf, buf_len);
720 QMessageBox::warning(
721 this,
722 "TTFautohint",
723 tr("The following error occurred while removing output file %1:\n")
724 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
725 + QString::fromLocal8Bit(buf),
726 QMessageBox::Ok,
727 QMessageBox::Ok);
730 return ret;
734 void
735 Main_GUI::run()
737 statusBar()->clearMessage();
739 QString input_name = QDir::fromNativeSeparators(input_line->text());
740 QString output_name = QDir::fromNativeSeparators(output_line->text());
741 if (!check_filenames(input_name, output_name))
742 return;
744 // we need C file descriptors for communication with TTF_autohint
745 FILE* input;
746 FILE* output;
748 again:
749 if (!open_files(input_name, &input, output_name, &output))
750 return;
752 QProgressDialog dialog;
753 dialog.setCancelButtonText(tr("Cancel"));
754 dialog.setMinimumDuration(1000);
755 dialog.setWindowModality(Qt::WindowModal);
757 const unsigned char* error_string;
758 TA_Info_Func info_func = info;
759 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
760 Info_Data info_data;
762 info_data.data = NULL; // must be deallocated after use
763 info_data.data_wide = NULL; // must be deallocated after use
764 info_data.data_len = 0;
765 info_data.data_wide_len = 0;
767 info_data.hinting_range_min = min_box->value();
768 info_data.hinting_range_max = max_box->value();
769 info_data.hinting_limit = no_limit_box->isChecked()
771 : limit_box->value();
773 info_data.gray_strong_stem_width = gray_box->isChecked();
774 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
775 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
777 info_data.increase_x_height = no_increase_box->isChecked()
779 : increase_box->value();
780 info_data.x_height_snapping_exceptions = x_height_snapping_exceptions;
782 info_data.windows_compatibility = wincomp_box->isChecked();
783 info_data.pre_hinting = pre_box->isChecked();
784 info_data.hint_composites = hint_box->isChecked();
785 info_data.symbol = symbol_box->isChecked();
786 info_data.dehint = dehint_box->isChecked();
788 strncpy(info_data.fallback_script,
789 script_names[fallback_box->currentIndex()].tag,
790 sizeof (info_data.fallback_script));
792 if (info_box->isChecked())
794 int ret = build_version_string(&info_data);
795 if (ret == 1)
796 QMessageBox::information(
797 this,
798 "TTFautohint",
799 tr("Can't allocate memory for <b>TTFautohint</b> options string"
800 " in <i>name</i> table."),
801 QMessageBox::Ok,
802 QMessageBox::Ok);
803 else if (ret == 2)
804 QMessageBox::information(
805 this,
806 "TTFautohint",
807 tr("<b>TTFautohint</b> options string"
808 " in <i>name</i> table too long."),
809 QMessageBox::Ok,
810 QMessageBox::Ok);
812 else
813 info_func = NULL;
815 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
817 TA_Error error =
818 TTF_autohint("in-file, out-file,"
819 "hinting-range-min, hinting-range-max,"
820 "hinting-limit,"
821 "gray-strong-stem-width,"
822 "gdi-cleartype-strong-stem-width,"
823 "dw-cleartype-strong-stem-width,"
824 "error-string,"
825 "progress-callback, progress-callback-data,"
826 "info-callback, info-callback-data,"
827 "ignore-restrictions,"
828 "windows-compatibility,"
829 "pre-hinting,"
830 "hint-composites,"
831 "increase-x-height,"
832 "x-height-snapping-exceptions,"
833 "fallback-script, symbol,"
834 "dehint",
835 input, output,
836 info_data.hinting_range_min, info_data.hinting_range_max,
837 info_data.hinting_limit,
838 info_data.gray_strong_stem_width,
839 info_data.gdi_cleartype_strong_stem_width,
840 info_data.dw_cleartype_strong_stem_width,
841 &error_string,
842 gui_progress, &gui_progress_data,
843 info_func, &info_data,
844 ignore_restrictions,
845 info_data.windows_compatibility,
846 info_data.pre_hinting,
847 info_data.hint_composites,
848 info_data.increase_x_height,
849 snapping_string.constData(),
850 info_data.fallback_script, info_data.symbol,
851 info_data.dehint);
853 if (info_box->isChecked())
855 free(info_data.data);
856 free(info_data.data_wide);
859 fclose(input);
860 fclose(output);
862 if (error)
864 if (handle_error(error, error_string, output_name))
865 goto again;
867 else
868 statusBar()->showMessage(tr("Auto-hinting finished."));
872 // XXX distances are specified in pixels,
873 // making the layout dependent on the output device resolution
874 void
875 Main_GUI::create_layout()
878 // file stuff
880 QCompleter* completer = new QCompleter(this);
881 QFileSystemModel* model = new QFileSystemModel(completer);
882 model->setRootPath(QDir::rootPath());
883 completer->setModel(model);
885 QLabel* input_label = new QLabel(tr("&Input File:"));
886 input_line = new Drag_Drop_Line_Edit;
887 input_button = new QPushButton(tr("Browse..."));
888 input_label->setBuddy(input_line);
889 // enforce rich text to get nice word wrapping
890 input_label->setToolTip(
891 tr("<b></b>The input file, either a TrueType font (TTF),"
892 " TrueType collection (TTC), or a TrueType-based OpenType font."));
893 input_line->setCompleter(completer);
895 QLabel* output_label = new QLabel(tr("&Output File:"));
896 output_line = new Drag_Drop_Line_Edit;
897 output_button = new QPushButton(tr("Browse..."));
898 output_label->setBuddy(output_line);
899 output_label->setToolTip(
900 tr("<b></b>The output file, which will be essentially identical"
901 " to the input font but will contain new, generated hints."));
902 output_line->setCompleter(completer);
904 // layout
905 QGridLayout* file_layout = new QGridLayout;
907 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
908 file_layout->addWidget(input_line, 0, 1);
909 file_layout->addWidget(input_button, 0, 2);
911 file_layout->setRowStretch(1, 1);
913 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
914 file_layout->addWidget(output_line, 2, 1);
915 file_layout->addWidget(output_button, 2, 2);
918 // minmax controls
920 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
921 min_box = new QSpinBox;
922 min_label->setBuddy(min_box);
923 min_label->setToolTip(
924 tr("The minimum PPEM value of the range for which"
925 " <b>TTFautohint</b> computes <i>hint sets</i>."
926 " A hint set for a given PPEM value hints this size optimally."
927 " The larger the range, the more hint sets are considered,"
928 " usually increasing the size of the bytecode.<br>"
929 "Note that changing this range doesn't influence"
930 " the <i>gasp</i> table:"
931 " Hinting is enabled for all sizes."));
932 min_box->setKeyboardTracking(false);
933 min_box->setRange(2, 10000);
935 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
936 max_box = new QSpinBox;
937 max_label->setBuddy(max_box);
938 max_label->setToolTip(
939 tr("The maximum PPEM value of the range for which"
940 " <b>TTFautohint</b> computes <i>hint sets</i>."
941 " A hint set for a given PPEM value hints this size optimally."
942 " The larger the range, the more hint sets are considered,"
943 " usually increasing the size of the bytecode.<br>"
944 "Note that changing this range doesn't influence"
945 " the <i>gasp</i> table:"
946 " Hinting is enabled for all sizes."));
947 max_box->setKeyboardTracking(false);
948 max_box->setRange(2, 10000);
951 // hinting and fallback controls
953 fallback_label = new QLabel(tr("Fallback &Script:"));
954 fallback_box = new QComboBox;
955 fallback_label->setBuddy(fallback_box);
956 fallback_label->setToolTip(
957 tr("This sets the fallback script module for glyphs"
958 " that <b>TTFautohint</b> can't map to a script automatically."));
959 for (int i = 0; script_names[i].tag; i++)
961 // XXX: how to provide translations?
962 fallback_box->insertItem(i,
963 QString("%1 (%2)")
964 .arg(script_names[i].tag)
965 .arg(script_names[i].description));
969 // hinting limit
971 limit_label = new QLabel(tr("Hinting &Limit:"));
972 limit_box = new QSpinBox;
973 limit_label->setBuddy(limit_box);
974 limit_label->setToolTip(
975 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
976 " sizes larger than this PPEM value are not hinted"
977 " (regardless of the values in the <i>gasp</i> table)."));
978 limit_box->setKeyboardTracking(false);
979 limit_box->setRange(2, 10000);
981 no_limit_box = new QCheckBox(tr("No Hinting Limit"), this);
982 no_limit_box->setToolTip(
983 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
984 " to the bytecode."));
987 // x height increase limit
989 increase_label = new QLabel(tr("x Height In&crease Limit:"));
990 increase_box = new QSpinBox;
991 increase_label->setBuddy(increase_box);
992 increase_label->setToolTip(
993 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
994 " where <i>n</i> is the value selected by this spin box,"
995 " round up the font's x&nbsp;height much more often than normally.<br>"
996 "Use this if holes in letters like <i>e</i> get filled,"
997 " for example."));
998 increase_box->setKeyboardTracking(false);
999 increase_box->setRange(6, 10000);
1001 no_increase_box = new QCheckBox(tr("No x Height Increase"), this);
1002 no_increase_box->setToolTip(
1003 tr("If switched on,"
1004 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
1007 // x height snapping exceptions
1009 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
1010 snapping_line = new Tooltip_Line_Edit;
1011 snapping_label->setBuddy(snapping_line);
1012 snapping_label->setToolTip(
1013 tr("<p>A list of comma separated PPEM values or value ranges"
1014 " at which no x&nbsp;height snapping shall be applied"
1015 " (x&nbsp;height snapping usually slightly increases"
1016 " the size of all glyphs).</p>"
1018 "Examples:<br>"
1019 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1020 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1021 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1022 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1025 // flags
1027 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1028 wincomp_box->setToolTip(
1029 tr("If switched on, add two artificial blue zones positioned at the"
1030 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1031 " (from the font's <i>OS/2</i> table)."
1032 " This option, usually in combination"
1033 " with value <tt>-</tt> (a single dash)"
1034 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1035 " should be used if those two <i>OS/2</i> values are tight,"
1036 " and you are experiencing clipping during rendering."));
1038 pre_box = new QCheckBox(tr("Pr&e-hinting"), this);
1039 pre_box->setToolTip(
1040 tr("If switched on, the original bytecode of the input font"
1041 " gets applied before <b>TTFautohint</b> starts processing"
1042 " the outlines of the glyphs."));
1044 hint_box = new QCheckBox(tr("Hint Co&mposites")
1045 + " ", this); // make label wider
1046 hint_box->setToolTip(
1047 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1048 " as a whole, including subglyphs."
1049 " Otherwise, glyph components get hinted separately.<br>"
1050 "Deactivating this flag reduces the bytecode size enormously,"
1051 " however, it might yield worse results."));
1053 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1054 symbol_box->setToolTip(
1055 tr("If switched on, <b>TTFautohint</b> uses default values"
1056 " for standard stem width and height"
1057 " instead of deriving these values from the input font.<br>"
1058 "Use this for fonts that don't contain glyphs"
1059 " of a (supported) script."));
1061 dehint_box = new QCheckBox(tr("&Dehint"), this);
1062 dehint_box->setToolTip(
1063 tr("<b></b>If set, remove all hints from the font."));
1065 info_box = new QCheckBox(tr("Add ttf&autohint Info"), this);
1066 info_box->setToolTip(
1067 tr("If switched on, information about <b>TTFautohint</b>"
1068 " and its calling parameters are added to the version string(s)"
1069 " (name ID&nbsp;5) in the <i>name</i> table."));
1072 // stem width and positioning
1074 stem_label = new QLabel(tr("Strong Stem &Width and Positioning:"));
1075 stem_label->setToolTip(
1076 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1077 " that can be selected for various hinting modes."
1079 "<p><i>strong</i> (checkbox set):"
1080 " Position horizontal stems and snap stem widths"
1081 " to integer pixel values. While making the output look crisper,"
1082 " outlines become more distorted.</p>"
1084 "<p><i>smooth</i> (checkbox not set):"
1085 " Use discrete values for horizontal stems and stem widths."
1086 " This only slightly increases the contrast"
1087 " but avoids larger outline distortion.</p>"));
1089 gray_box = new QCheckBox(tr("Grayscale"), this);
1090 gray_box->setToolTip(
1091 tr("<b></b>Grayscale rendering, no ClearType activated."));
1092 stem_label->setBuddy(gray_box);
1094 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1095 gdi_box->setToolTip(
1096 tr("GDI ClearType rendering,"
1097 " introduced in 2000 for Windows XP.<br>"
1098 "The rasterizer version (as returned by the"
1099 " GETINFO bytecode instruction) is in the range"
1100 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1101 "Along the vertical axis, this mode behaves like B/W rendering."));
1103 dw_box = new QCheckBox(tr("DW ClearType"), this);
1104 dw_box->setToolTip(
1105 tr("DirectWrite ClearType rendering,"
1106 " introduced in 2008 for Windows Vista.<br>"
1107 "The rasterizer version (as returned by the"
1108 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1109 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1110 "Smooth rendering along the vertical axis."));
1113 // running
1115 run_button = new QPushButton(" "
1116 + tr("&Run")
1117 + " "); // make label wider
1120 // the whole gui
1122 QGridLayout* gui_layout = new QGridLayout;
1123 QFrame* hline = new QFrame;
1124 hline->setFrameShape(QFrame::HLine);
1125 int row = 0; // this counter simplifies inserting new items
1127 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1128 gui_layout->setRowStretch(row++, 1);
1130 gui_layout->addLayout(file_layout, row, 0, row, -1);
1131 gui_layout->setRowStretch(row++, 1);
1133 gui_layout->addWidget(hline, row, 0, row, -1);
1134 gui_layout->setRowStretch(row++, 1);
1136 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1137 gui_layout->setRowStretch(row++, 1);
1139 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1140 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1141 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1142 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1144 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1145 gui_layout->setRowStretch(row++, 1);
1147 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1148 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1150 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1151 gui_layout->setRowStretch(row++, 1);
1153 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1154 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1155 gui_layout->addWidget(no_limit_box, row++, 1);
1157 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1158 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1159 gui_layout->addWidget(no_increase_box, row++, 1);
1161 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1162 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1164 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1165 gui_layout->setRowStretch(row++, 1);
1167 gui_layout->addWidget(wincomp_box, row++, 1);
1168 gui_layout->addWidget(pre_box, row++, 1);
1169 gui_layout->addWidget(hint_box, row++, 1);
1170 gui_layout->addWidget(symbol_box, row++, 1);
1171 gui_layout->addWidget(dehint_box, row++, 1);
1172 gui_layout->addWidget(info_box, row++, 1);
1174 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1175 gui_layout->setRowStretch(row++, 1);
1177 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1178 gui_layout->addWidget(gray_box, row++, 1);
1179 gui_layout->addWidget(gdi_box, row++, 1);
1180 gui_layout->addWidget(dw_box, row++, 1);
1182 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1183 gui_layout->setRowStretch(row++, 1);
1185 gui_layout->addWidget(run_button, row++, 1, Qt::AlignRight);
1187 // create dummy widget to register layout
1188 QWidget* main_widget = new QWidget;
1189 main_widget->setLayout(gui_layout);
1190 setCentralWidget(main_widget);
1191 setWindowTitle("TTFautohint");
1195 void
1196 Main_GUI::create_connections()
1198 connect(input_button, SIGNAL(clicked()), this,
1199 SLOT(browse_input()));
1200 connect(output_button, SIGNAL(clicked()), this,
1201 SLOT(browse_output()));
1203 connect(input_line, SIGNAL(textChanged(QString)), this,
1204 SLOT(check_run()));
1205 connect(output_line, SIGNAL(textChanged(QString)), this,
1206 SLOT(check_run()));
1208 connect(input_line, SIGNAL(editingFinished()), this,
1209 SLOT(absolute_input()));
1210 connect(output_line, SIGNAL(editingFinished()), this,
1211 SLOT(absolute_output()));
1213 connect(min_box, SIGNAL(valueChanged(int)), this,
1214 SLOT(check_min()));
1215 connect(max_box, SIGNAL(valueChanged(int)), this,
1216 SLOT(check_max()));
1218 connect(limit_box, SIGNAL(valueChanged(int)), this,
1219 SLOT(check_limit()));
1220 connect(no_limit_box, SIGNAL(clicked()), this,
1221 SLOT(check_no_limit()));
1223 connect(no_increase_box, SIGNAL(clicked()), this,
1224 SLOT(check_no_increase()));
1226 connect(snapping_line, SIGNAL(editingFinished()), this,
1227 SLOT(check_number_set()));
1228 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1229 SLOT(clear_status_bar()));
1231 connect(dehint_box, SIGNAL(clicked()), this,
1232 SLOT(check_dehint()));
1234 connect(run_button, SIGNAL(clicked()), this,
1235 SLOT(run()));
1239 void
1240 Main_GUI::create_actions()
1242 exit_act = new QAction(tr("E&xit"), this);
1243 exit_act->setShortcuts(QKeySequence::Quit);
1244 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1246 about_act = new QAction(tr("&About"), this);
1247 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1249 about_Qt_act = new QAction(tr("About &Qt"), this);
1250 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1254 void
1255 Main_GUI::create_menus()
1257 file_menu = menuBar()->addMenu(tr("&File"));
1258 file_menu->addAction(exit_act);
1260 help_menu = menuBar()->addMenu(tr("&Help"));
1261 help_menu->addAction(about_act);
1262 help_menu->addAction(about_Qt_act);
1266 void
1267 Main_GUI::create_status_bar()
1269 statusBar()->showMessage("");
1273 void
1274 Main_GUI::set_defaults()
1276 min_box->setValue(hinting_range_min);
1277 max_box->setValue(hinting_range_max);
1279 fallback_box->setCurrentIndex(fallback_script_idx);
1281 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1282 // handle command line option `--hinting-limit=0'
1283 if (!hinting_limit)
1285 hinting_limit = max_box->value();
1286 no_limit_box->setChecked(true);
1289 increase_box->setValue(increase_x_height ? increase_x_height
1290 : TA_INCREASE_X_HEIGHT);
1291 // handle command line option `--increase-x-height=0'
1292 if (!increase_x_height)
1294 increase_x_height = TA_INCREASE_X_HEIGHT;
1295 no_increase_box->setChecked(true);
1298 snapping_line->setText(x_height_snapping_exceptions_string);
1300 if (windows_compatibility)
1301 wincomp_box->setChecked(true);
1302 if (pre_hinting)
1303 pre_box->setChecked(true);
1304 if (hint_composites)
1305 hint_box->setChecked(true);
1306 if (symbol)
1307 symbol_box->setChecked(true);
1308 if (dehint)
1309 dehint_box->setChecked(true);
1310 if (!no_info)
1311 info_box->setChecked(true);
1313 if (gray_strong_stem_width)
1314 gray_box->setChecked(true);
1315 if (gdi_cleartype_strong_stem_width)
1316 gdi_box->setChecked(true);
1317 if (dw_cleartype_strong_stem_width)
1318 dw_box->setChecked(true);
1320 run_button->setEnabled(false);
1322 check_min();
1323 check_max();
1324 check_limit();
1326 check_no_limit();
1327 check_no_increase();
1328 check_number_set();
1330 // do this last since it disables almost everything
1331 check_dehint();
1335 void
1336 Main_GUI::read_settings()
1338 QSettings settings;
1339 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1340 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1341 // resize(size);
1342 // move(pos);
1346 void
1347 Main_GUI::write_settings()
1349 QSettings settings;
1350 // settings.setValue("pos", pos());
1351 // settings.setValue("size", size());
1354 // end of maingui.cpp