Implement option `--dehint' in front-ends.
[ttfautohint.git] / frontend / maingui.cpp
blob8f4ab9d242ab6e7bf7b2ef560fae01b4e3d677bf
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 Main_GUI::Main_GUI(int range_min,
41 int range_max,
42 int limit,
43 bool gray,
44 bool gdi,
45 bool dw,
46 int increase,
47 const char* exceptions,
48 bool ignore,
49 bool wincomp,
50 bool pre,
51 bool components,
52 bool no,
53 int fallback,
54 bool symb,
55 bool dh)
56 : hinting_range_min(range_min),
57 hinting_range_max(range_max),
58 hinting_limit(limit),
59 gray_strong_stem_width(gray),
60 gdi_cleartype_strong_stem_width(gdi),
61 dw_cleartype_strong_stem_width(dw),
62 increase_x_height(increase),
63 x_height_snapping_exceptions_string(exceptions),
64 ignore_restrictions(ignore),
65 windows_compatibility(wincomp),
66 pre_hinting(pre),
67 hint_with_components(components),
68 no_info(no),
69 latin_fallback(fallback),
70 symbol(symb),
71 dehint(dh)
73 x_height_snapping_exceptions = NULL;
75 create_layout();
76 create_connections();
77 create_actions();
78 create_menus();
79 create_status_bar();
81 set_defaults();
82 read_settings();
84 setUnifiedTitleAndToolBarOnMac(true);
86 // XXX register translations somewhere and loop over them
87 if (QLocale::system().name() == "en_US")
88 locale = new QLocale;
89 else
90 locale = new QLocale(QLocale::C);
94 Main_GUI::~Main_GUI()
96 number_set_free(x_height_snapping_exceptions);
100 // overloading
102 void
103 Main_GUI::closeEvent(QCloseEvent* event)
105 write_settings();
106 event->accept();
110 void
111 Main_GUI::about()
113 QMessageBox::about(this,
114 tr("About TTFautohint"),
115 tr("<p>This is <b>TTFautohint</b> version %1<br>"
116 " Copyright %2 2011-2013<br>"
117 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
119 "<p><b>TTFautohint</b> adds new auto-generated hints"
120 " to a TrueType font or TrueType collection.</p>"
122 "<p>License:"
123 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
124 " License (FTL)</a> or"
125 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
126 " GPLv2</a></p>")
127 .arg(VERSION)
128 .arg(QChar(0xA9)));
132 void
133 Main_GUI::browse_input()
135 // XXX remember last directory
136 QString file = QFileDialog::getOpenFileName(
137 this,
138 tr("Open Input File"),
139 QDir::homePath(),
140 "");
141 if (!file.isEmpty())
142 input_line->setText(QDir::toNativeSeparators(file));
146 void
147 Main_GUI::browse_output()
149 // XXX remember last directory
150 QString file = QFileDialog::getSaveFileName(
151 this,
152 tr("Open Output File"),
153 QDir::homePath(),
154 "");
156 if (!file.isEmpty())
157 output_line->setText(QDir::toNativeSeparators(file));
161 void
162 Main_GUI::check_min()
164 int min = min_box->value();
165 int max = max_box->value();
166 int limit = limit_box->value();
167 if (min > max)
168 max_box->setValue(min);
169 if (min > limit)
170 limit_box->setValue(min);
174 void
175 Main_GUI::check_max()
177 int min = min_box->value();
178 int max = max_box->value();
179 int limit = limit_box->value();
180 if (max < min)
181 min_box->setValue(max);
182 if (max > limit)
183 limit_box->setValue(max);
187 void
188 Main_GUI::check_limit()
190 int min = min_box->value();
191 int max = max_box->value();
192 int limit = limit_box->value();
193 if (limit < max)
194 max_box->setValue(limit);
195 if (limit < min)
196 min_box->setValue(limit);
200 void
201 Main_GUI::check_dehint()
203 if (dehint_box->isChecked())
205 min_label->setEnabled(false);
206 min_box->setEnabled(false);
208 max_label->setEnabled(false);
209 max_box->setEnabled(false);
211 fallback_label->setEnabled(false);
212 fallback_box->setEnabled(false);
214 no_limit_box->setEnabled(false);
215 limit_label->setEnabled(false);
216 limit_box->setEnabled(false);
218 no_increase_box->setEnabled(false);
219 increase_label->setEnabled(false);
220 increase_box->setEnabled(false);
222 snapping_label->setEnabled(false);
223 snapping_line->setEnabled(false);
225 wincomp_box->setEnabled(false);
226 pre_box->setEnabled(false);
227 hint_box->setEnabled(false);
228 symbol_box->setEnabled(false);
230 stem_label->setEnabled(false);
231 gray_box->setEnabled(false);
232 gdi_box->setEnabled(false);
233 dw_box->setEnabled(false);
235 else
237 min_label->setEnabled(true);
238 min_box->setEnabled(true);
240 max_label->setEnabled(true);
241 max_box->setEnabled(true);
243 fallback_label->setEnabled(true);
244 fallback_box->setEnabled(true);
246 no_limit_box->setEnabled(true);
247 check_no_limit();
249 no_increase_box->setEnabled(true);
250 check_no_increase();
252 snapping_label->setEnabled(true);
253 snapping_line->setEnabled(true);
255 wincomp_box->setEnabled(true);
256 pre_box->setEnabled(true);
257 hint_box->setEnabled(true);
258 symbol_box->setEnabled(true);
260 stem_label->setEnabled(true);
261 gray_box->setEnabled(true);
262 gdi_box->setEnabled(true);
263 dw_box->setEnabled(true);
268 void
269 Main_GUI::check_no_limit()
271 if (no_limit_box->isChecked())
273 limit_label->setEnabled(false);
274 limit_box->setEnabled(false);
276 else
278 limit_label->setEnabled(true);
279 limit_box->setEnabled(true);
284 void
285 Main_GUI::check_no_increase()
287 if (no_increase_box->isChecked())
289 increase_label->setEnabled(false);
290 increase_box->setEnabled(false);
292 else
294 increase_label->setEnabled(true);
295 increase_box->setEnabled(true);
300 void
301 Main_GUI::check_run()
303 if (input_line->text().isEmpty() || output_line->text().isEmpty())
304 run_button->setEnabled(false);
305 else
306 run_button->setEnabled(true);
310 void
311 Main_GUI::absolute_input()
313 QString input_name = QDir::fromNativeSeparators(input_line->text());
314 if (!input_name.isEmpty()
315 && QDir::isRelativePath(input_name))
317 QDir cur_path(QDir::currentPath() + "/" + input_name);
318 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
323 void
324 Main_GUI::absolute_output()
326 QString output_name = QDir::fromNativeSeparators(output_line->text());
327 if (!output_name.isEmpty()
328 && QDir::isRelativePath(output_name))
330 QDir cur_path(QDir::currentPath() + "/" + output_name);
331 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
336 void
337 Main_GUI::check_number_set()
339 QString text = snapping_line->text();
340 QString qs;
342 // construct ASCII string from arbitrary Unicode data;
343 // the idea is to accept, say, CJK fullwidth digits also
344 for (int i = 0; i < text.size(); i++)
346 QChar c = text.at(i);
348 int digit = c.digitValue();
349 if (digit >= 0)
350 qs += QString::number(digit);
351 else if (c.isSpace())
352 qs += ' ';
353 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
354 // to the `-' key in some Japanese input methods
355 else if (c.category() == QChar::Punctuation_Dash
356 || c == QChar(0x30FC))
357 qs += '-';
358 // various Unicode COMMA characters,
359 // including representation forms
360 else if (c == QChar(',')
361 || c == QChar(0x055D)
362 || c == QChar(0x060C)
363 || c == QChar(0x07F8)
364 || c == QChar(0x1363)
365 || c == QChar(0x1802)
366 || c == QChar(0x1808)
367 || c == QChar(0x3001)
368 || c == QChar(0xA4FE)
369 || c == QChar(0xA60D)
370 || c == QChar(0xA6F5)
371 || c == QChar(0xFE10)
372 || c == QChar(0xFE11)
373 || c == QChar(0xFE50)
374 || c == QChar(0xFE51)
375 || c == QChar(0xFF0C)
376 || c == QChar(0xFF64))
377 qs += ',';
378 else
379 qs += c; // we do error handling below
382 if (x_height_snapping_exceptions)
383 number_set_free(x_height_snapping_exceptions);
385 QByteArray str = qs.toLocal8Bit();
386 const char* s = number_set_parse(str.constData(),
387 &x_height_snapping_exceptions,
388 6, 0x7FFF);
389 if (s && *s)
391 statusBar()->setStyleSheet("color: red;");
392 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
393 statusBar()->showMessage(
394 tr("allocation error"));
395 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
396 statusBar()->showMessage(
397 tr("invalid character (use digits, dashes, commas, and spaces)"));
398 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
399 statusBar()->showMessage(
400 tr("overflow"));
401 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
402 statusBar()->showMessage(
403 tr("invalid range (minimum is 6, maximum is 32767)"));
404 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
405 statusBar()->showMessage(
406 tr("overlapping ranges"));
407 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
408 statusBar()->showMessage(
409 tr("values und ranges must be specified in ascending order"));
411 snapping_line->setText(qs);
412 snapping_line->setFocus(Qt::OtherFocusReason);
413 snapping_line->setCursorPosition(s - str.constData());
415 x_height_snapping_exceptions = NULL;
417 else
419 // normalize if there is no error
420 char* new_str = number_set_show(x_height_snapping_exceptions,
421 6, 0x7FFF);
422 snapping_line->setText(new_str);
423 free(new_str);
428 void
429 Main_GUI::clear_status_bar()
431 statusBar()->clearMessage();
432 statusBar()->setStyleSheet("");
437 Main_GUI::check_filenames(const QString& input_name,
438 const QString& output_name)
440 if (!QFile::exists(input_name))
442 QMessageBox::warning(
443 this,
444 "TTFautohint",
445 tr("The file %1 cannot be found.")
446 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
447 QMessageBox::Ok,
448 QMessageBox::Ok);
449 return 0;
452 if (input_name == output_name)
454 QMessageBox::warning(
455 this,
456 "TTFautohint",
457 tr("Input and output file names must be different."),
458 QMessageBox::Ok,
459 QMessageBox::Ok);
460 return 0;
463 if (QFile::exists(output_name))
465 int ret = QMessageBox::warning(
466 this,
467 "TTFautohint",
468 tr("The file %1 already exists.\n"
469 "Overwrite?")
470 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
471 QMessageBox::Yes | QMessageBox::No,
472 QMessageBox::No);
473 if (ret == QMessageBox::No)
474 return 0;
477 return 1;
482 Main_GUI::open_files(const QString& input_name,
483 FILE** in,
484 const QString& output_name,
485 FILE** out)
487 const int buf_len = 1024;
488 char buf[buf_len];
490 *in = fopen(qPrintable(input_name), "rb");
491 if (!*in)
493 strerror_r(errno, buf, buf_len);
494 QMessageBox::warning(
495 this,
496 "TTFautohint",
497 tr("The following error occurred while opening input file %1:\n")
498 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
499 + QString::fromLocal8Bit(buf),
500 QMessageBox::Ok,
501 QMessageBox::Ok);
502 return 0;
505 *out = fopen(qPrintable(output_name), "wb");
506 if (!*out)
508 strerror_r(errno, buf, buf_len);
509 QMessageBox::warning(
510 this,
511 "TTFautohint",
512 tr("The following error occurred while opening output file %1:\n")
513 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
514 + QString::fromLocal8Bit(buf),
515 QMessageBox::Ok,
516 QMessageBox::Ok);
517 return 0;
520 return 1;
524 extern "C" {
526 struct GUI_Progress_Data
528 long last_sfnt;
529 bool begin;
530 QProgressDialog* dialog;
535 gui_progress(long curr_idx,
536 long num_glyphs,
537 long curr_sfnt,
538 long num_sfnts,
539 void* user)
541 GUI_Progress_Data* data = (GUI_Progress_Data*)user;
543 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
545 data->dialog->setLabelText(QCoreApplication::translate(
546 "GuiProgress",
547 "Auto-hinting subfont %1 of %2"
548 " with %3 glyphs...")
549 .arg(curr_sfnt + 1)
550 .arg(num_sfnts)
551 .arg(num_glyphs));
553 if (curr_sfnt + 1 == num_sfnts)
555 data->dialog->setAutoReset(true);
556 data->dialog->setAutoClose(true);
558 else
560 data->dialog->setAutoReset(false);
561 data->dialog->setAutoClose(false);
564 data->last_sfnt = curr_sfnt;
565 data->begin = true;
568 if (data->begin)
570 if (num_sfnts == 1)
571 data->dialog->setLabelText(QCoreApplication::translate(
572 "GuiProgress",
573 "Auto-hinting %1 glyphs...")
574 .arg(num_glyphs));
575 data->dialog->setMaximum(num_glyphs - 1);
577 data->begin = false;
580 data->dialog->setValue(curr_idx);
582 if (data->dialog->wasCanceled())
583 return 1;
585 return 0;
588 } // extern "C"
591 // return value 1 indicates a retry
594 Main_GUI::handle_error(TA_Error error,
595 const unsigned char* error_string,
596 QString output_name)
598 int ret = 0;
600 if (error == TA_Err_Canceled)
602 else if (error == TA_Err_Invalid_FreeType_Version)
603 QMessageBox::critical(
604 this,
605 "TTFautohint",
606 tr("FreeType version 2.4.5 or higher is needed.\n"
607 "Are you perhaps using a wrong FreeType DLL?"),
608 QMessageBox::Ok,
609 QMessageBox::Ok);
610 else if (error == TA_Err_Invalid_Font_Type)
611 QMessageBox::warning(
612 this,
613 "TTFautohint",
614 tr("This font is not a valid font"
615 " in SFNT format with TrueType outlines.\n"
616 "In particular, CFF outlines are not supported."),
617 QMessageBox::Ok,
618 QMessageBox::Ok);
619 else if (error == TA_Err_Already_Processed)
620 QMessageBox::warning(
621 this,
622 "TTFautohint",
623 tr("This font has already been processed by TTFautohint."),
624 QMessageBox::Ok,
625 QMessageBox::Ok);
626 else if (error == TA_Err_Missing_Legal_Permission)
628 int yesno = QMessageBox::warning(
629 this,
630 "TTFautohint",
631 tr("Bit 1 in the %1 field of the %2 table is set:"
632 " This font must not be modified"
633 " without permission of the legal owner.\n"
634 "Do you have such a permission?")
635 .arg(QUOTE_STRING_LITERAL("fsType"))
636 .arg(QUOTE_STRING_LITERAL("OS/2")),
637 QMessageBox::Yes | QMessageBox::No,
638 QMessageBox::No);
639 if (yesno == QMessageBox::Yes)
641 ignore_restrictions = true;
642 ret = 1;
645 else if (error == TA_Err_Missing_Unicode_CMap)
646 QMessageBox::warning(
647 this,
648 "TTFautohint",
649 tr("No Unicode character map."),
650 QMessageBox::Ok,
651 QMessageBox::Ok);
652 else if (error == TA_Err_Missing_Symbol_CMap)
653 QMessageBox::warning(
654 this,
655 "TTFautohint",
656 tr("No symbol character map."),
657 QMessageBox::Ok,
658 QMessageBox::Ok);
659 else if (error == TA_Err_Missing_Glyph)
660 QMessageBox::warning(
661 this,
662 "TTFautohint",
663 tr("No glyph for the key character"
664 " to derive standard stem width and height.\n"
665 "For the latin script, this key character is %1 (U+006F).\n"
666 "\n"
667 "Set the %2 checkbox if you want to circumvent this test.")
668 .arg(QUOTE_STRING_LITERAL("o"))
669 .arg(QUOTE_STRING_LITERAL("symbol")),
670 QMessageBox::Ok,
671 QMessageBox::Ok);
672 else
673 QMessageBox::warning(
674 this,
675 "TTFautohint",
676 tr("Error code 0x%1 while autohinting font:\n")
677 .arg(error, 2, 16, QLatin1Char('0'))
678 + QString::fromLocal8Bit((const char*)error_string),
679 QMessageBox::Ok,
680 QMessageBox::Ok);
682 if (QFile::exists(output_name) && remove(qPrintable(output_name)))
684 const int buf_len = 1024;
685 char buf[buf_len];
687 strerror_r(errno, buf, buf_len);
688 QMessageBox::warning(
689 this,
690 "TTFautohint",
691 tr("The following error occurred while removing output file %1:\n")
692 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
693 + QString::fromLocal8Bit(buf),
694 QMessageBox::Ok,
695 QMessageBox::Ok);
698 return ret;
702 void
703 Main_GUI::run()
705 statusBar()->clearMessage();
707 QString input_name = QDir::fromNativeSeparators(input_line->text());
708 QString output_name = QDir::fromNativeSeparators(output_line->text());
709 if (!check_filenames(input_name, output_name))
710 return;
712 // we need C file descriptors for communication with TTF_autohint
713 FILE* input;
714 FILE* output;
716 again:
717 if (!open_files(input_name, &input, output_name, &output))
718 return;
720 QProgressDialog dialog;
721 dialog.setCancelButtonText(tr("Cancel"));
722 dialog.setMinimumDuration(1000);
723 dialog.setWindowModality(Qt::WindowModal);
725 const unsigned char* error_string;
726 TA_Info_Func info_func = info;
727 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
728 Info_Data info_data;
730 info_data.data = NULL; // must be deallocated after use
731 info_data.data_wide = NULL; // must be deallocated after use
732 info_data.data_len = 0;
733 info_data.data_wide_len = 0;
735 info_data.hinting_range_min = min_box->value();
736 info_data.hinting_range_max = max_box->value();
737 info_data.hinting_limit = no_limit_box->isChecked()
739 : limit_box->value();
741 info_data.gray_strong_stem_width = gray_box->isChecked();
742 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
743 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
745 info_data.increase_x_height = no_increase_box->isChecked()
747 : increase_box->value();
748 info_data.x_height_snapping_exceptions = x_height_snapping_exceptions;
750 info_data.windows_compatibility = wincomp_box->isChecked();
751 info_data.pre_hinting = pre_box->isChecked();
752 info_data.hint_with_components = hint_box->isChecked();
753 info_data.latin_fallback = fallback_box->currentIndex();
754 info_data.symbol = symbol_box->isChecked();
755 info_data.dehint = dehint_box->isChecked();
757 if (info_box->isChecked())
759 int ret = build_version_string(&info_data);
760 if (ret == 1)
761 QMessageBox::information(
762 this,
763 "TTFautohint",
764 tr("Can't allocate memory for <b>TTFautohint</b> options string"
765 " in <i>name</i> table."),
766 QMessageBox::Ok,
767 QMessageBox::Ok);
768 else if (ret == 2)
769 QMessageBox::information(
770 this,
771 "TTFautohint",
772 tr("<b>TTFautohint</b> options string"
773 " in <i>name</i> table too long."),
774 QMessageBox::Ok,
775 QMessageBox::Ok);
777 else
778 info_func = NULL;
780 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
782 TA_Error error =
783 TTF_autohint("in-file, out-file,"
784 "hinting-range-min, hinting-range-max,"
785 "hinting-limit,"
786 "gray-strong-stem-width,"
787 "gdi-cleartype-strong-stem-width,"
788 "dw-cleartype-strong-stem-width,"
789 "error-string,"
790 "progress-callback, progress-callback-data,"
791 "info-callback, info-callback-data,"
792 "ignore-restrictions,"
793 "windows-compatibility,"
794 "pre-hinting,"
795 "hint-with-components,"
796 "increase-x-height,"
797 "x-height-snapping-exceptions,"
798 "fallback-script, symbol,"
799 "dehint",
800 input, output,
801 info_data.hinting_range_min, info_data.hinting_range_max,
802 info_data.hinting_limit,
803 info_data.gray_strong_stem_width,
804 info_data.gdi_cleartype_strong_stem_width,
805 info_data.dw_cleartype_strong_stem_width,
806 &error_string,
807 gui_progress, &gui_progress_data,
808 info_func, &info_data,
809 ignore_restrictions,
810 info_data.windows_compatibility,
811 info_data.pre_hinting,
812 info_data.hint_with_components,
813 info_data.increase_x_height,
814 snapping_string.constData(),
815 info_data.latin_fallback, info_data.symbol,
816 info_data.dehint);
818 if (info_box->isChecked())
820 free(info_data.data);
821 free(info_data.data_wide);
824 fclose(input);
825 fclose(output);
827 if (error)
829 if (handle_error(error, error_string, output_name))
830 goto again;
832 else
833 statusBar()->showMessage(tr("Auto-hinting finished."));
837 // XXX distances are specified in pixels,
838 // making the layout dependent on the output device resolution
839 void
840 Main_GUI::create_layout()
843 // file stuff
845 QCompleter* completer = new QCompleter(this);
846 QFileSystemModel* model = new QFileSystemModel(completer);
847 model->setRootPath(QDir::rootPath());
848 completer->setModel(model);
850 QLabel* input_label = new QLabel(tr("&Input File:"));
851 input_line = new Drag_Drop_Line_Edit;
852 input_button = new QPushButton(tr("Browse..."));
853 input_label->setBuddy(input_line);
854 // enforce rich text to get nice word wrapping
855 input_label->setToolTip(
856 tr("<b></b>The input file, either a TrueType font (TTF),"
857 " TrueType collection (TTC), or a TrueType-based OpenType font."));
858 input_line->setCompleter(completer);
860 QLabel* output_label = new QLabel(tr("&Output File:"));
861 output_line = new Drag_Drop_Line_Edit;
862 output_button = new QPushButton(tr("Browse..."));
863 output_label->setBuddy(output_line);
864 output_label->setToolTip(
865 tr("<b></b>The output file, which will be essentially identical"
866 " to the input font but will contain new, generated hints."));
867 output_line->setCompleter(completer);
869 // layout
870 QGridLayout* file_layout = new QGridLayout;
872 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
873 file_layout->addWidget(input_line, 0, 1);
874 file_layout->addWidget(input_button, 0, 2);
876 file_layout->setRowStretch(1, 1);
878 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
879 file_layout->addWidget(output_line, 2, 1);
880 file_layout->addWidget(output_button, 2, 2);
883 // minmax controls
885 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
886 min_box = new QSpinBox;
887 min_label->setBuddy(min_box);
888 min_label->setToolTip(
889 tr("The minimum PPEM value of the range for which"
890 " <b>TTFautohint</b> computes <i>hint sets</i>."
891 " A hint set for a given PPEM value hints this size optimally."
892 " The larger the range, the more hint sets are considered,"
893 " usually increasing the size of the bytecode.<br>"
894 "Note that changing this range doesn't influence"
895 " the <i>gasp</i> table:"
896 " Hinting is enabled for all sizes."));
897 min_box->setKeyboardTracking(false);
898 min_box->setRange(2, 10000);
900 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
901 max_box = new QSpinBox;
902 max_label->setBuddy(max_box);
903 max_label->setToolTip(
904 tr("The maximum PPEM value of the range for which"
905 " <b>TTFautohint</b> computes <i>hint sets</i>."
906 " A hint set for a given PPEM value hints this size optimally."
907 " The larger the range, the more hint sets are considered,"
908 " usually increasing the size of the bytecode.<br>"
909 "Note that changing this range doesn't influence"
910 " the <i>gasp</i> table:"
911 " Hinting is enabled for all sizes."));
912 max_box->setKeyboardTracking(false);
913 max_box->setRange(2, 10000);
916 // hinting and fallback controls
918 fallback_label = new QLabel(tr("Fallback &Script:"));
919 fallback_box = new QComboBox;
920 fallback_label->setBuddy(fallback_box);
921 fallback_label->setToolTip(
922 tr("This sets the fallback script module for glyphs"
923 " which <b>TTFautohint</b> can't map to a script automatically."));
924 fallback_box->insertItem(0, tr("None"));
925 fallback_box->insertItem(1, tr("Latin"));
928 // hinting limit
930 limit_label = new QLabel(tr("Hinting &Limit:"));
931 limit_box = new QSpinBox;
932 limit_label->setBuddy(limit_box);
933 limit_label->setToolTip(
934 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
935 " sizes larger than this PPEM value are not hinted"
936 " (regardless of the values in the <i>gasp</i> table)."));
937 limit_box->setKeyboardTracking(false);
938 limit_box->setRange(2, 10000);
940 no_limit_box = new QCheckBox(tr("No Hinting Limit"), this);
941 no_limit_box->setToolTip(
942 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
943 " to the bytecode."));
946 // x height increase limit
948 increase_label = new QLabel(tr("x Height In&crease Limit:"));
949 increase_box = new QSpinBox;
950 increase_label->setBuddy(increase_box);
951 increase_label->setToolTip(
952 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
953 " where <i>n</i> is the value selected by this spin box,"
954 " round up the font's x&nbsp;height much more often than normally.<br>"
955 "Use this if holes in letters like <i>e</i> get filled,"
956 " for example."));
957 increase_box->setKeyboardTracking(false);
958 increase_box->setRange(6, 10000);
960 no_increase_box = new QCheckBox(tr("No x Height Increase"), this);
961 no_increase_box->setToolTip(
962 tr("If switched on,"
963 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
966 // x height snapping exceptions
968 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
969 snapping_line = new Tooltip_Line_Edit;
970 snapping_label->setBuddy(snapping_line);
971 snapping_label->setToolTip(
972 tr("<p>A list of comma separated PPEM values or value ranges"
973 " at which no x&nbsp;height snapping shall be applied"
974 " (x&nbsp;height snapping usually slightly increases"
975 " the size of all glyphs).</p>"
977 "Examples:<br>"
978 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
979 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
980 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
981 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
984 // flags
986 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
987 wincomp_box->setToolTip(
988 tr("If switched on, add two artificial blue zones positioned at the"
989 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
990 " (from the font's <i>OS/2</i> table)."
991 " This option, usually in combination"
992 " with value <tt>-</tt> (a single dash)"
993 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
994 " should be used if those two <i>OS/2</i> values are tight,"
995 " and you are experiencing clipping during rendering."));
997 pre_box = new QCheckBox(tr("Pr&e-hinting"), this);
998 pre_box->setToolTip(
999 tr("If switched on, the original bytecode of the input font"
1000 " gets applied before <b>TTFautohint</b> starts processing"
1001 " the outlines of the glyphs."));
1003 hint_box = new QCheckBox(tr("Hint With Co&mponents")
1004 + " ", this); // make label wider
1005 hint_box->setToolTip(
1006 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1007 " as a whole, including subglyphs."
1008 " Otherwise, glyph components get hinted separately.<br>"
1009 "Deactivating this flag reduces the bytecode size enormously,"
1010 " however, it might yield worse results."));
1012 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1013 symbol_box->setToolTip(
1014 tr("If switched on, <b>TTFautohint</b> uses default values"
1015 " for standard stem width and height"
1016 " instead of deriving these values from the input font.<br>"
1017 "Use this for fonts which don't contain glyphs"
1018 " of a (supported) script."));
1020 dehint_box = new QCheckBox(tr("&Dehint"), this);
1021 dehint_box->setToolTip(
1022 tr("<b></b>If set, remove all hints from the font."));
1024 info_box = new QCheckBox(tr("Add ttf&autohint Info"), this);
1025 info_box->setToolTip(
1026 tr("If switched on, information about <b>TTFautohint</b>"
1027 " and its calling parameters are added to the version string(s)"
1028 " (name ID&nbsp;5) in the <i>name</i> table."));
1031 // stem width and positioning
1033 stem_label = new QLabel(tr("Strong Stem &Width and Positioning:"));
1034 stem_label->setToolTip(
1035 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1036 " which can be selected for various hinting modes."
1038 "<p><i>strong:</i> Position horizontal stems and snap stem widths"
1039 " to integer pixel values. While making the output look crisper,"
1040 " outlines become more distorted.</p>"
1042 "<p><i>smooth:</i> Use discrete values for horizontal stems"
1043 " and stem widths. This only slightly increases the contrast"
1044 " but avoids larger outline distortion.</p>"));
1046 gray_box = new QCheckBox(tr("Grayscale"), this);
1047 gray_box->setToolTip(
1048 tr("<b></b>Grayscale rendering, no ClearType activated."));
1049 stem_label->setBuddy(gray_box);
1051 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1052 gdi_box->setToolTip(
1053 tr("GDI ClearType rendering,"
1054 " introduced in 2000 for Windows XP.<br>"
1055 "The rasterizer version (as returned by the"
1056 " GETINFO bytecode instruction) is in the range"
1057 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1058 "Along the vertical axis, this mode behaves like B/W rendering."));
1060 dw_box = new QCheckBox(tr("DW ClearType"), this);
1061 dw_box->setToolTip(
1062 tr("DirectWrite ClearType rendering,"
1063 " introduced in 2008 for Windows Vista.<br>"
1064 "The rasterizer version (as returned by the"
1065 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1066 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1067 "Smooth rendering along the vertical axis."));
1070 // running
1072 run_button = new QPushButton(" "
1073 + tr("&Run")
1074 + " "); // make label wider
1077 // the whole gui
1079 QGridLayout* gui_layout = new QGridLayout;
1080 QFrame* hline = new QFrame;
1081 hline->setFrameShape(QFrame::HLine);
1082 int row = 0; // this counter simplifies inserting new items
1084 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1085 gui_layout->setRowStretch(row++, 1);
1087 gui_layout->addLayout(file_layout, row, 0, row, -1);
1088 gui_layout->setRowStretch(row++, 1);
1090 gui_layout->addWidget(hline, row, 0, row, -1);
1091 gui_layout->setRowStretch(row++, 1);
1093 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1094 gui_layout->setRowStretch(row++, 1);
1096 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1097 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1098 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1099 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1101 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1102 gui_layout->setRowStretch(row++, 1);
1104 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1105 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1107 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1108 gui_layout->setRowStretch(row++, 1);
1110 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1111 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1112 gui_layout->addWidget(no_limit_box, row++, 1);
1114 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1115 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1116 gui_layout->addWidget(no_increase_box, row++, 1);
1118 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1119 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1121 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1122 gui_layout->setRowStretch(row++, 1);
1124 gui_layout->addWidget(wincomp_box, row++, 1);
1125 gui_layout->addWidget(pre_box, row++, 1);
1126 gui_layout->addWidget(hint_box, row++, 1);
1127 gui_layout->addWidget(symbol_box, row++, 1);
1128 gui_layout->addWidget(dehint_box, row++, 1);
1129 gui_layout->addWidget(info_box, row++, 1);
1131 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1132 gui_layout->setRowStretch(row++, 1);
1134 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1135 gui_layout->addWidget(gray_box, row++, 1);
1136 gui_layout->addWidget(gdi_box, row++, 1);
1137 gui_layout->addWidget(dw_box, row++, 1);
1139 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1140 gui_layout->setRowStretch(row++, 1);
1142 gui_layout->addWidget(run_button, row++, 1, Qt::AlignRight);
1144 // create dummy widget to register layout
1145 QWidget* main_widget = new QWidget;
1146 main_widget->setLayout(gui_layout);
1147 setCentralWidget(main_widget);
1148 setWindowTitle("TTFautohint");
1152 void
1153 Main_GUI::create_connections()
1155 connect(input_button, SIGNAL(clicked()), this,
1156 SLOT(browse_input()));
1157 connect(output_button, SIGNAL(clicked()), this,
1158 SLOT(browse_output()));
1160 connect(input_line, SIGNAL(textChanged(QString)), this,
1161 SLOT(check_run()));
1162 connect(output_line, SIGNAL(textChanged(QString)), this,
1163 SLOT(check_run()));
1165 connect(input_line, SIGNAL(editingFinished()), this,
1166 SLOT(absolute_input()));
1167 connect(output_line, SIGNAL(editingFinished()), this,
1168 SLOT(absolute_output()));
1170 connect(min_box, SIGNAL(valueChanged(int)), this,
1171 SLOT(check_min()));
1172 connect(max_box, SIGNAL(valueChanged(int)), this,
1173 SLOT(check_max()));
1175 connect(limit_box, SIGNAL(valueChanged(int)), this,
1176 SLOT(check_limit()));
1177 connect(no_limit_box, SIGNAL(clicked()), this,
1178 SLOT(check_no_limit()));
1180 connect(no_increase_box, SIGNAL(clicked()), this,
1181 SLOT(check_no_increase()));
1183 connect(snapping_line, SIGNAL(editingFinished()), this,
1184 SLOT(check_number_set()));
1185 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1186 SLOT(clear_status_bar()));
1188 connect(dehint_box, SIGNAL(clicked()), this,
1189 SLOT(check_dehint()));
1191 connect(run_button, SIGNAL(clicked()), this,
1192 SLOT(run()));
1196 void
1197 Main_GUI::create_actions()
1199 exit_act = new QAction(tr("E&xit"), this);
1200 exit_act->setShortcuts(QKeySequence::Quit);
1201 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1203 about_act = new QAction(tr("&About"), this);
1204 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1206 about_Qt_act = new QAction(tr("About &Qt"), this);
1207 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1211 void
1212 Main_GUI::create_menus()
1214 file_menu = menuBar()->addMenu(tr("&File"));
1215 file_menu->addAction(exit_act);
1217 help_menu = menuBar()->addMenu(tr("&Help"));
1218 help_menu->addAction(about_act);
1219 help_menu->addAction(about_Qt_act);
1223 void
1224 Main_GUI::create_status_bar()
1226 statusBar()->showMessage("");
1230 void
1231 Main_GUI::set_defaults()
1233 min_box->setValue(hinting_range_min);
1234 max_box->setValue(hinting_range_max);
1236 fallback_box->setCurrentIndex(latin_fallback);
1238 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1239 // handle command line option `--hinting-limit=0'
1240 if (!hinting_limit)
1242 hinting_limit = max_box->value();
1243 no_limit_box->setChecked(true);
1246 increase_box->setValue(increase_x_height ? increase_x_height
1247 : TA_INCREASE_X_HEIGHT);
1248 // handle command line option `--increase-x-height=0'
1249 if (!increase_x_height)
1251 increase_x_height = TA_INCREASE_X_HEIGHT;
1252 no_increase_box->setChecked(true);
1255 snapping_line->setText(x_height_snapping_exceptions_string);
1257 if (windows_compatibility)
1258 wincomp_box->setChecked(true);
1259 if (pre_hinting)
1260 pre_box->setChecked(true);
1261 if (hint_with_components)
1262 hint_box->setChecked(true);
1263 if (symbol)
1264 symbol_box->setChecked(true);
1265 if (dehint)
1266 dehint_box->setChecked(true);
1267 if (!no_info)
1268 info_box->setChecked(true);
1270 if (gray_strong_stem_width)
1271 gray_box->setChecked(true);
1272 if (gdi_cleartype_strong_stem_width)
1273 gdi_box->setChecked(true);
1274 if (dw_cleartype_strong_stem_width)
1275 dw_box->setChecked(true);
1277 run_button->setEnabled(false);
1279 check_min();
1280 check_max();
1281 check_limit();
1283 check_no_limit();
1284 check_no_increase();
1285 check_number_set();
1287 // do this last since it disables almost everything
1288 check_dehint();
1292 void
1293 Main_GUI::read_settings()
1295 QSettings settings;
1296 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1297 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1298 // resize(size);
1299 // move(pos);
1303 void
1304 Main_GUI::write_settings()
1306 QSettings settings;
1307 // settings.setValue("pos", pos());
1308 // settings.setValue("size", size());
1311 // end of maingui.cpp