3 // Copyright (C) 2012-2016 by Werner Lemberg.
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.
10 // The file `COPYING' mentioned in the previous paragraph is distributed
11 // with the ttfautohint library.
26 #include <ttfautohint.h>
29 // XXX Qt 4.8 bug: locale->quoteString("foo")
30 // inserts wrongly encoded quote characters
31 // into rich text QString
32 #if HAVE_QT_QUOTESTRING
33 # define QUOTE_STRING(x) locale->quoteString(x)
34 # define QUOTE_STRING_LITERAL(x) locale->quoteString(x)
36 # define QUOTE_STRING(x) "\"" + x + "\""
37 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
41 // Shorthand for `tr' using a local `TRDOMAIN'.
42 #define Tr(text) QCoreApplication::translate(TRDOMAIN, text)
45 typedef struct Tag_Names_
48 const char* description
;
52 // the available script tags and its descriptions are directly extracted
53 // from `ttfautohint-scripts.h'
55 #define SCRIPT(s, S, d, h, H, ss) \
58 const Tag_Names script_names
[] =
60 #include <ttfautohint-scripts.h>
65 // the available feature tags and its descriptions are directly extracted
66 // from `ttfautohint-coverages.h'
68 #define COVERAGE(n, N, d, t, t1, t2, t3, t4) \
71 const Tag_Names feature_names
[] =
73 #include <ttfautohint-coverages.h>
79 // a: Add ttf&autohint Info
80 // b: Add TTFA info ta&ble
81 // c: x Height In&crease Limit / No x Height In&crease
83 // e: Control Instructions Fil&e
85 // g: Stron&g Stem Width and Positioning
88 // j: Ad&just Subglyphs
89 // k: Fallbac&k Script
90 // l: Hinting &Limit / No Hinting &Limit
91 // m: Hint Co&mposites
92 // n: Hint Set Range Mi&nimum
94 // p: Windows Com&patibility
97 // s: Fallback &Stem Width / Default Fallback &Stem Width
98 // t: x Height Snapping Excep&tions
101 // w: &Watch Input Files
102 // x: Hint Set Range Ma&ximum
106 Main_GUI::Main_GUI(bool horizontal_layout
,
114 const char* exceptions
,
123 const char* fallback
,
129 : hinting_range_min(range_min
),
130 hinting_range_max(range_max
),
131 hinting_limit(limit
),
132 gray_strong_stem_width(gray
),
133 gdi_cleartype_strong_stem_width(gdi
),
134 dw_cleartype_strong_stem_width(dw
),
135 increase_x_height(increase
),
136 x_height_snapping_exceptions_string(exceptions
),
137 fallback_stem_width(stem_width
),
138 ignore_restrictions(ignore
),
139 windows_compatibility(wincomp
),
140 adjust_subglyphs(adjust
),
141 hint_composites(composites
),
143 detailed_info(detailed
),
144 fallback_scaling(scaling
),
145 family_suffix(suffix
),
150 fallback_do_scale(1),
155 for (i
= 0; script_names
[i
].tag
; i
++)
157 if (!strcmp("latn", script_names
[i
].tag
))
159 if (!strcmp("none", script_names
[i
].tag
))
163 // map default script and fallback script tags to indices
164 for (i
= 0; script_names
[i
].tag
; i
++)
165 if (!strcmp(dflt
, script_names
[i
].tag
))
167 default_script_idx
= script_names
[i
].tag
? i
: latn_script_idx
;
169 for (i
= 0; script_names
[i
].tag
; i
++)
170 if (!strcmp(fallback
, script_names
[i
].tag
))
172 fallback_script_idx
= script_names
[i
].tag
? i
: none_script_idx
;
174 x_height_snapping_exceptions
= NULL
;
176 // if the current input files have been updated
177 // we wait a given time interval, then we reload the files
178 file_watcher
= new QFileSystemWatcher(this);
179 timer
= new QTimer(this);
180 timer
->setInterval(1000); // XXX make this configurable
181 timer
->setSingleShot(true);
182 fileinfo_input_file
.setCaching(false);
183 fileinfo_control_file
.setCaching(false);
185 // XXX register translations somewhere and loop over them
186 if (QLocale::system().name() == "en_US")
187 locale
= new QLocale
;
189 locale
= new QLocale(QLocale::C
);
191 // For real numbers (both parsing and displaying) we only use `.' as the
192 // decimal separator; similarly, we don't want localized formats like a
193 // thousands separator for any number.
194 setlocale(LC_NUMERIC
, "C");
196 create_layout(horizontal_layout
);
197 create_connections();
205 setUnifiedTitleAndToolBarOnMac(true);
209 Main_GUI::~Main_GUI()
211 number_set_free(x_height_snapping_exceptions
);
218 Main_GUI::closeEvent(QCloseEvent
* event
)
228 QMessageBox::about(this,
229 tr("About TTFautohint"),
230 tr("<p>This is <b>TTFautohint</b> version %1<br>"
231 " Copyright %2 2011-2016<br>"
232 " by Werner Lemberg <tt><wl@gnu.org></tt></p>"
234 "<p><b>TTFautohint</b> adds new auto-generated hints"
235 " to a TrueType font or TrueType collection.</p>"
238 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT'>FreeType"
239 " License (FTL)</a> or"
240 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT'>GNU"
248 Main_GUI::browse_input()
250 // XXX remember last directory
251 QString file
= QFileDialog::getOpenFileName(
253 tr("Open Input File"),
258 input_line
->setText(QDir::toNativeSeparators(file
));
263 Main_GUI::browse_output()
265 // XXX remember last directory
266 QString file
= QFileDialog::getSaveFileName(
268 tr("Open Output File"),
273 output_line
->setText(QDir::toNativeSeparators(file
));
278 Main_GUI::browse_control()
280 // XXX remember last directory
281 QString file
= QFileDialog::getOpenFileName(
283 tr("Open Control INstructions File"),
288 control_line
->setText(QDir::toNativeSeparators(file
));
293 Main_GUI::check_min()
295 int min
= min_box
->value();
296 int max
= max_box
->value();
297 int limit
= limit_box
->value();
299 max_box
->setValue(min
);
301 limit_box
->setValue(min
);
306 Main_GUI::check_max()
308 int min
= min_box
->value();
309 int max
= max_box
->value();
310 int limit
= limit_box
->value();
312 min_box
->setValue(max
);
314 limit_box
->setValue(max
);
319 Main_GUI::check_limit()
321 int min
= min_box
->value();
322 int max
= max_box
->value();
323 int limit
= limit_box
->value();
325 max_box
->setValue(limit
);
327 min_box
->setValue(limit
);
332 Main_GUI::check_dehint()
334 if (dehint_box
->isChecked())
336 min_label
->setEnabled(false);
337 min_box
->setEnabled(false);
339 max_label
->setEnabled(false);
340 max_box
->setEnabled(false);
342 default_label
->setEnabled(false);
343 default_box
->setEnabled(false);
344 fallback_label
->setEnabled(false);
345 fallback_hint_or_scale_box
->setEnabled(false);
346 fallback_box
->setEnabled(false);
348 no_limit_box
->setEnabled(false);
349 limit_label
->setEnabled(false);
350 limit_box
->setEnabled(false);
352 no_increase_box
->setEnabled(false);
353 increase_label
->setEnabled(false);
354 increase_box
->setEnabled(false);
356 snapping_label
->setEnabled(false);
357 snapping_line
->setEnabled(false);
359 default_stem_width_box
->setEnabled(false);
360 stem_width_label
->setEnabled(false);
361 stem_width_box
->setEnabled(false);
363 wincomp_box
->setEnabled(false);
364 adjust_box
->setEnabled(false);
365 hint_box
->setEnabled(false);
366 symbol_box
->setEnabled(false);
368 stem_label
->setEnabled(false);
369 gray_box
->setEnabled(false);
370 gdi_box
->setEnabled(false);
371 dw_box
->setEnabled(false);
375 min_label
->setEnabled(true);
376 min_box
->setEnabled(true);
378 max_label
->setEnabled(true);
379 max_box
->setEnabled(true);
381 default_label
->setEnabled(true);
382 default_box
->setEnabled(true);
383 fallback_label
->setEnabled(true);
384 fallback_hint_or_scale_box
->setEnabled(true);
385 fallback_box
->setEnabled(true);
387 no_limit_box
->setEnabled(true);
390 no_increase_box
->setEnabled(true);
393 snapping_label
->setEnabled(true);
394 snapping_line
->setEnabled(true);
396 default_stem_width_box
->setEnabled(true);
397 check_default_stem_width();
399 wincomp_box
->setEnabled(true);
400 adjust_box
->setEnabled(true);
401 hint_box
->setEnabled(true);
402 symbol_box
->setEnabled(true);
404 stem_label
->setEnabled(true);
405 gray_box
->setEnabled(true);
406 gdi_box
->setEnabled(true);
407 dw_box
->setEnabled(true);
413 Main_GUI::check_no_limit()
415 if (no_limit_box
->isChecked())
417 limit_label
->setEnabled(false);
418 limit_label
->setText(limit_label_text
);
419 limit_box
->setEnabled(false);
420 no_limit_box
->setText(no_limit_box_text_with_key
);
424 limit_label
->setEnabled(true);
425 limit_label
->setText(limit_label_text_with_key
);
426 limit_box
->setEnabled(true);
427 no_limit_box
->setText(no_limit_box_text
);
433 Main_GUI::check_no_increase()
435 if (no_increase_box
->isChecked())
437 increase_label
->setEnabled(false);
438 increase_label
->setText(increase_label_text
);
439 increase_box
->setEnabled(false);
440 no_increase_box
->setText(no_increase_box_text_with_key
);
444 increase_label
->setEnabled(true);
445 increase_label
->setText(increase_label_text_with_key
);
446 increase_box
->setEnabled(true);
447 no_increase_box
->setText(no_increase_box_text
);
453 Main_GUI::check_default_stem_width()
455 if (default_stem_width_box
->isChecked())
457 stem_width_label
->setEnabled(false);
458 stem_width_label
->setText(stem_width_label_text
);
459 stem_width_box
->setEnabled(false);
460 default_stem_width_box
->setText(default_stem_width_box_text_with_key
);
464 stem_width_label
->setEnabled(true);
465 stem_width_label
->setText(stem_width_label_text_with_key
);
466 stem_width_box
->setEnabled(true);
467 default_stem_width_box
->setText(default_stem_width_box_text
);
473 Main_GUI::check_run()
475 if (input_line
->text().isEmpty() || output_line
->text().isEmpty())
476 run_button
->setEnabled(false);
478 run_button
->setEnabled(true);
483 Main_GUI::absolute_input()
485 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
486 if (!input_name
.isEmpty()
487 && QDir::isRelativePath(input_name
))
489 QDir
cur_path(QDir::currentPath() + "/" + input_name
);
490 input_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
496 Main_GUI::absolute_output()
498 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
499 if (!output_name
.isEmpty()
500 && QDir::isRelativePath(output_name
))
502 QDir
cur_path(QDir::currentPath() + "/" + output_name
);
503 output_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
509 Main_GUI::absolute_control()
511 QString control_name
= QDir::fromNativeSeparators(control_line
->text());
512 if (!control_name
.isEmpty()
513 && QDir::isRelativePath(control_name
))
515 QDir
cur_path(QDir::currentPath() + "/" + control_name
);
516 control_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
522 Main_GUI::check_number_set()
524 QString text
= snapping_line
->text();
527 // construct ASCII string from arbitrary Unicode data;
528 // the idea is to accept, say, CJK fullwidth digits also
529 for (int i
= 0; i
< text
.size(); i
++)
531 QChar c
= text
.at(i
);
533 int digit
= c
.digitValue();
535 qs
+= QString::number(digit
);
536 else if (c
.isSpace())
538 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
539 // to the `-' key in some Japanese input methods
540 else if (c
.category() == QChar::Punctuation_Dash
541 || c
== QChar(0x30FC))
543 // various Unicode COMMA characters,
544 // including representation forms
545 else if (c
== QChar(',')
546 || c
== QChar(0x055D)
547 || c
== QChar(0x060C)
548 || c
== QChar(0x07F8)
549 || c
== QChar(0x1363)
550 || c
== QChar(0x1802)
551 || c
== QChar(0x1808)
552 || c
== QChar(0x3001)
553 || c
== QChar(0xA4FE)
554 || c
== QChar(0xA60D)
555 || c
== QChar(0xA6F5)
556 || c
== QChar(0xFE10)
557 || c
== QChar(0xFE11)
558 || c
== QChar(0xFE50)
559 || c
== QChar(0xFE51)
560 || c
== QChar(0xFF0C)
561 || c
== QChar(0xFF64))
564 qs
+= c
; // we do error handling below
567 if (x_height_snapping_exceptions
)
568 number_set_free(x_height_snapping_exceptions
);
570 QByteArray str
= qs
.toLocal8Bit();
571 const char* s
= number_set_parse(str
.constData(),
572 &x_height_snapping_exceptions
,
576 statusBar()->setStyleSheet("color: red;");
577 if (x_height_snapping_exceptions
== NUMBERSET_ALLOCATION_ERROR
)
578 statusBar()->showMessage(
579 tr("allocation error"));
580 else if (x_height_snapping_exceptions
== NUMBERSET_INVALID_CHARACTER
)
581 statusBar()->showMessage(
582 tr("invalid character (use digits, dashes, commas, and spaces)"));
583 else if (x_height_snapping_exceptions
== NUMBERSET_OVERFLOW
)
584 statusBar()->showMessage(
586 else if (x_height_snapping_exceptions
== NUMBERSET_INVALID_RANGE
)
587 statusBar()->showMessage(
588 tr("invalid range (minimum is 6, maximum is 32767)"));
589 else if (x_height_snapping_exceptions
== NUMBERSET_OVERLAPPING_RANGES
)
590 statusBar()->showMessage(
591 tr("overlapping ranges"));
592 else if (x_height_snapping_exceptions
== NUMBERSET_NOT_ASCENDING
)
593 statusBar()->showMessage(
594 tr("values und ranges must be specified in ascending order"));
596 snapping_line
->setText(qs
);
597 snapping_line
->setFocus(Qt::OtherFocusReason
);
598 snapping_line
->setCursorPosition(s
- str
.constData());
600 x_height_snapping_exceptions
= NULL
;
604 // normalize if there is no error
605 char* new_str
= number_set_show(x_height_snapping_exceptions
,
607 snapping_line
->setText(new_str
);
614 Main_GUI::check_family_suffix()
616 QString text
= family_suffix_line
->text();
617 const char* s
= qPrintable(text
);
619 if (const char* pos
= ::check_family_suffix(s
))
621 statusBar()->setStyleSheet("color: red;");
622 statusBar()->showMessage(
623 tr("invalid character (use printable ASCII except %()/<>[]{})"));
624 family_suffix_line
->setFocus(Qt::OtherFocusReason
);
625 family_suffix_line
->setCursorPosition(pos
- s
);
631 Main_GUI::clear_status_bar()
633 statusBar()->clearMessage();
634 statusBar()->setStyleSheet("");
639 Main_GUI::check_filenames(const QString
& input_name
,
640 const QString
& output_name
,
641 const QString
& control_name
)
643 if (!QFile::exists(input_name
))
645 QMessageBox::warning(
648 tr("The file %1 cannot be found.")
649 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
))),
655 if (input_name
== output_name
)
657 QMessageBox::warning(
660 tr("Input and output file names must be different."),
666 // silently overwrite if watching is active
667 if (QFile::exists(output_name
) && !watch_box
->isChecked())
669 int ret
= QMessageBox::warning(
672 tr("The file %1 already exists.\n"
674 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
))),
675 QMessageBox::Yes
| QMessageBox::No
,
677 if (ret
== QMessageBox::No
)
681 if (!control_name
.isEmpty() && !QFile::exists(control_name
))
683 QMessageBox::warning(
686 tr("The file %1 cannot be found.")
687 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name
))),
698 Main_GUI::open_files(const QString
& input_name
,
700 const QString
& output_name
,
702 const QString
& control_name
,
705 const int buf_len
= 1024;
708 *in
= fopen(qPrintable(input_name
), "rb");
711 strerror_r(errno
, buf
, buf_len
);
712 QMessageBox::warning(
715 tr("The following error occurred while opening input file %1:\n")
716 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
)))
717 + QString::fromLocal8Bit(buf
),
723 *out
= fopen(qPrintable(output_name
), "wb");
726 strerror_r(errno
, buf
, buf_len
);
727 QMessageBox::warning(
730 tr("The following error occurred while opening output file %1:\n")
731 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
732 + QString::fromLocal8Bit(buf
),
738 if (!control_name
.isEmpty())
740 *control
= fopen(qPrintable(control_name
), "r");
743 strerror_r(errno
, buf
, buf_len
);
744 QMessageBox::warning(
747 tr("The following error occurred"
748 " while opening control instructions file %1:\n")
749 .arg(QUOTE_STRING(QDir::toNativeSeparators(control_name
)))
750 + QString::fromLocal8Bit(buf
),
764 Main_GUI::check_watch()
766 if (watch_box
->isChecked())
768 // file watching gets started in the `run' function
777 Main_GUI::start_timer()
779 // we delay the file watching action, mainly to ensure
780 // that newly generated files have been completely written to disk
787 Main_GUI::stop_watching()
790 if (!fileinfo_input_file
.fileName().isEmpty())
791 file_watcher
->removePath(fileinfo_input_file
.fileName());
792 if (!fileinfo_control_file
.fileName().isEmpty())
793 file_watcher
->removePath(fileinfo_control_file
.fileName());
798 Main_GUI::watch_files()
800 if (fileinfo_input_file
.exists()
801 && fileinfo_input_file
.isReadable()
802 && (fileinfo_control_file
.fileName().isEmpty()
804 : (fileinfo_control_file
.exists()
805 && fileinfo_control_file
.isReadable())))
807 QDateTime modified_input
= fileinfo_input_file
.lastModified();
808 QDateTime modified_control
= fileinfo_control_file
.lastModified();
809 // XXX make this configurable
810 if (datetime_input_file
.msecsTo(modified_input
) > 1000
811 || datetime_control_file
.msecsTo(modified_control
) > 1000)
814 run(); // this function sets `datetime_XXX'
818 // restart timer for symlinks
819 if (watch_box
->isChecked())
821 if (fileinfo_input_file
.isSymLink()
822 || fileinfo_control_file
.isSymLink())
829 if (check
== DoCheck
)
831 else if (check
== CheckLater
)
834 run(); // let this routine handle all errors
841 struct GUI_Progress_Data
845 QProgressDialog
* dialog
;
850 #define TRDOMAIN "GuiProgress"
853 gui_progress(long curr_idx
,
859 GUI_Progress_Data
* data
= static_cast<GUI_Progress_Data
*>(user
);
861 if (num_sfnts
> 1 && curr_sfnt
!= data
->last_sfnt
)
863 data
->dialog
->setLabelText(Tr("Auto-hinting subfont %1 of %2"
864 " with %3 glyphs...")
869 if (curr_sfnt
+ 1 == num_sfnts
)
871 data
->dialog
->setAutoReset(true);
872 data
->dialog
->setAutoClose(true);
876 data
->dialog
->setAutoReset(false);
877 data
->dialog
->setAutoClose(false);
880 data
->last_sfnt
= curr_sfnt
;
887 data
->dialog
->setLabelText(Tr("Auto-hinting %1 glyphs...")
889 data
->dialog
->setMaximum(num_glyphs
- 1);
894 data
->dialog
->setValue(curr_idx
);
896 if (data
->dialog
->wasCanceled())
903 struct GUI_Error_Data
908 QString control_name
;
909 int* ignore_restrictions_p
;
915 #define TRDOMAIN "GuiError"
918 gui_error(TA_Error error
,
919 const char* error_string
,
920 unsigned int errlinenum
,
925 GUI_Error_Data
* data
= static_cast<GUI_Error_Data
*>(user
);
926 QLocale
* locale
= data
->locale
; // for QUOTE_STRING_LITERAL
931 if (error
== TA_Err_Canceled
)
933 else if (error
== TA_Err_Invalid_FreeType_Version
)
934 QMessageBox::critical(
937 Tr("FreeType version 2.4.5 or higher is needed.\n"
938 "Are you perhaps using a wrong FreeType DLL?"),
941 else if (error
== TA_Err_Invalid_Font_Type
)
942 QMessageBox::warning(
945 Tr("This font is not a valid font"
946 " in SFNT format with TrueType outlines.\n"
947 "In particular, CFF outlines are not supported."),
950 else if (error
== TA_Err_Already_Processed
)
951 QMessageBox::warning(
954 Tr("This font has already been processed by TTFautohint."),
957 else if (error
== TA_Err_Missing_Legal_Permission
)
959 int yesno
= QMessageBox::warning(
962 Tr("Bit 1 in the %1 field of the %2 table is set:"
963 " This font must not be modified"
964 " without permission of the legal owner.\n"
965 "Do you have such a permission?")
966 .arg(QUOTE_STRING_LITERAL("fsType"))
967 .arg(QUOTE_STRING_LITERAL("OS/2")),
968 QMessageBox::Yes
| QMessageBox::No
,
970 if (yesno
== QMessageBox::Yes
)
972 *data
->ignore_restrictions_p
= true;
976 else if (error
== TA_Err_Missing_Unicode_CMap
)
977 QMessageBox::warning(
980 Tr("The input font doesn't contain a Unicode character map.\n"
981 "Maybe you haven't set the %1 checkbox?")
982 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
985 else if (error
== TA_Err_Missing_Symbol_CMap
)
986 QMessageBox::warning(
989 Tr("The input font does neither contain a symbol"
990 " nor a character map."),
993 else if (error
== TA_Err_Missing_Glyph
)
994 QMessageBox::warning(
997 Tr("No glyph for a standard character"
998 " to derive standard width and height.\n"
999 "Please check the documentation for a list of"
1000 " script-specific standard characters.\n"
1002 "Set the %1 checkbox if you want to circumvent this test.")
1003 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
1006 else if (error
>= 0x200 && error
< 0x300)
1007 QMessageBox::warning(
1010 QString::fromLocal8Bit("%1:%2:%3: %4 (0x%5)<br>"
1013 .arg(data
->control_name
)
1015 .arg(int(errpos
- errline
+ 1))
1017 .arg(error
, 2, 16, QLatin1Char('0'))
1019 .arg('^', int(errpos
- errline
+ 1))
1020 .replace(" ", " "),
1024 QMessageBox::warning(
1027 Tr("Error code 0x%1 while autohinting font:\n")
1028 .arg(error
, 2, 16, QLatin1Char('0'))
1029 + QString::fromLocal8Bit(error_string
),
1033 if (QFile::exists(data
->output_name
)
1034 && remove(qPrintable(data
->output_name
)))
1036 const int buf_len
= 1024;
1039 strerror_r(errno
, buf
, buf_len
);
1040 QMessageBox::warning(
1043 Tr("The following error occurred while removing output file %1:\n")
1044 .arg(QUOTE_STRING(QDir::toNativeSeparators(data
->output_name
)))
1045 + QString::fromLocal8Bit(buf
),
1059 if (check
== CheckLater
)
1065 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
1066 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
1067 QString control_name
= QDir::fromNativeSeparators(control_line
->text());
1068 if (!check_filenames(input_name
, output_name
, control_name
))
1074 // we need C file descriptors for communication with TTF_autohint
1080 if (!open_files(input_name
, &input
,
1081 output_name
, &output
,
1082 control_name
, &control
))
1088 QProgressDialog dialog
;
1089 dialog
.setCancelButtonText(tr("Cancel"));
1090 dialog
.setMinimumDuration(1000);
1091 dialog
.setWindowModality(Qt::WindowModal
);
1093 TA_Info_Func info_func
= info
;
1094 TA_Info_Post_Func info_post_func
= info_post
;
1095 GUI_Progress_Data gui_progress_data
= {-1, true, &dialog
};
1096 GUI_Error_Data gui_error_data
= {this, locale
, output_name
, control_name
,
1097 &ignore_restrictions
, false};
1099 fileinfo_input_file
.setFile(input_name
);
1100 fileinfo_control_file
.setFile(control_name
);
1102 Info_Data info_data
;
1104 info_data
.info_string
= NULL
; // must be deallocated after use
1105 info_data
.info_string_wide
= NULL
; // must be deallocated after use
1106 info_data
.info_string_len
= 0;
1107 info_data
.info_string_wide_len
= 0;
1109 info_data
.control_name
= qPrintable(fileinfo_control_file
.fileName());
1111 info_data
.hinting_range_min
= min_box
->value();
1112 info_data
.hinting_range_max
= max_box
->value();
1113 info_data
.hinting_limit
= no_limit_box
->isChecked()
1115 : limit_box
->value();
1117 info_data
.gray_strong_stem_width
= gray_box
->isChecked();
1118 info_data
.gdi_cleartype_strong_stem_width
= gdi_box
->isChecked();
1119 info_data
.dw_cleartype_strong_stem_width
= dw_box
->isChecked();
1121 info_data
.increase_x_height
= no_increase_box
->isChecked()
1123 : increase_box
->value();
1124 info_data
.x_height_snapping_exceptions_string
=
1125 qPrintable(x_height_snapping_exceptions_string
);
1127 info_data
.family_suffix
=
1128 family_suffix_line
->text().toLocal8Bit().constData();
1129 info_data
.family_data_head
= NULL
;
1131 info_data
.fallback_stem_width
= default_stem_width_box
->isChecked()
1133 : stem_width_box
->value();
1135 info_data
.windows_compatibility
= wincomp_box
->isChecked();
1136 info_data
.adjust_subglyphs
= adjust_box
->isChecked();
1137 info_data
.hint_composites
= hint_box
->isChecked();
1138 info_data
.symbol
= symbol_box
->isChecked();
1139 info_data
.fallback_scaling
= fallback_hint_or_scale_box
->currentIndex();
1140 info_data
.no_info
= info_box
->currentIndex() == 0;
1141 info_data
.detailed_info
= info_box
->currentIndex() == 2;
1142 info_data
.dehint
= dehint_box
->isChecked();
1143 info_data
.TTFA_info
= TTFA_box
->isChecked();
1145 strncpy(info_data
.default_script
,
1146 script_names
[default_box
->currentIndex()].tag
,
1147 sizeof (info_data
.default_script
));
1148 strncpy(info_data
.fallback_script
,
1149 script_names
[fallback_box
->currentIndex()].tag
,
1150 sizeof (info_data
.fallback_script
));
1152 if (info_box
->currentIndex())
1154 int ret
= build_version_string(&info_data
);
1156 QMessageBox::information(
1159 tr("Can't allocate memory for <b>TTFautohint</b> options string"
1160 " in <i>name</i> table."),
1164 QMessageBox::information(
1167 tr("<b>TTFautohint</b> options string"
1168 " in <i>name</i> table too long."),
1173 if (!*info_data
.family_suffix
)
1174 info_post_func
= NULL
;
1176 if (info_data
.symbol
1177 && info_data
.fallback_stem_width
1178 && info_data
.fallback_scaling
)
1179 QMessageBox::information(
1182 tr("Setting a fallback stem width for a symbol font"
1183 " with fallback scaling only has no effect."),
1187 datetime_input_file
= fileinfo_input_file
.lastModified();
1188 datetime_control_file
= fileinfo_control_file
.lastModified();
1190 QByteArray snapping_string
= snapping_line
->text().toLocal8Bit();
1193 TTF_autohint("in-file, out-file, control-file,"
1194 "hinting-range-min, hinting-range-max,"
1196 "gray-strong-stem-width,"
1197 "gdi-cleartype-strong-stem-width,"
1198 "dw-cleartype-strong-stem-width,"
1199 "progress-callback, progress-callback-data,"
1200 "error-callback, error-callback-data,"
1201 "info-callback, info-post-callback, info-callback-data,"
1202 "ignore-restrictions,"
1203 "windows-compatibility,"
1206 "increase-x-height,"
1207 "x-height-snapping-exceptions, fallback-stem-width,"
1209 "fallback-script, fallback-scaling,"
1210 "symbol, dehint, TTFA-info",
1211 input
, output
, control
,
1212 info_data
.hinting_range_min
, info_data
.hinting_range_max
,
1213 info_data
.hinting_limit
,
1214 info_data
.gray_strong_stem_width
,
1215 info_data
.gdi_cleartype_strong_stem_width
,
1216 info_data
.dw_cleartype_strong_stem_width
,
1217 gui_progress
, &gui_progress_data
,
1218 gui_error
, &gui_error_data
,
1219 info_func
, info_post_func
, &info_data
,
1220 ignore_restrictions
,
1221 info_data
.windows_compatibility
,
1222 info_data
.adjust_subglyphs
,
1223 info_data
.hint_composites
,
1224 info_data
.increase_x_height
,
1225 snapping_string
.constData(), info_data
.fallback_stem_width
,
1226 info_data
.default_script
,
1227 info_data
.fallback_script
, info_data
.fallback_scaling
,
1228 info_data
.symbol
, info_data
.dehint
, info_data
.TTFA_info
);
1230 if (info_box
->currentIndex())
1232 free(info_data
.info_string
);
1233 free(info_data
.info_string_wide
);
1243 // retry if there is a user request to do so (handled in `gui_error')
1244 if (gui_error_data
.retry
)
1251 statusBar()->showMessage(tr("Auto-hinting finished")
1253 + QDateTime::currentDateTime()
1254 .toString(Qt::TextDate
)
1257 // we have successfully processed a file;
1258 // start file watching now if requested
1259 if (watch_box
->isChecked())
1263 // Qt's file watcher doesn't handle symlinks;
1264 // we thus fall back to polling
1265 if (fileinfo_input_file
.isSymLink()
1266 || fileinfo_control_file
.isSymLink())
1270 file_watcher
->addPath(input_name
);
1271 file_watcher
->addPath(control_name
);
1278 // XXX distances are specified in pixels,
1279 // making the layout dependent on the output device resolution
1281 Main_GUI::create_layout(bool horizontal_layout
)
1286 QCompleter
* completer
= new QCompleter(this);
1287 QFileSystemModel
* model
= new QFileSystemModel(completer
);
1288 model
->setRootPath(QDir::rootPath());
1289 completer
->setModel(model
);
1291 input_label
= new QLabel(tr("&Input File:"));
1292 input_line
= new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE
);
1293 input_button
= new QPushButton(tr("Browse..."));
1294 input_label
->setBuddy(input_line
);
1295 // enforce rich text to get nice word wrapping
1296 input_label
->setToolTip(
1297 tr("<b></b>The input file, either a TrueType font (TTF),"
1298 " TrueType collection (TTC), or a TrueType-based OpenType font."));
1299 input_line
->setCompleter(completer
);
1301 output_label
= new QLabel(tr("&Output File:"));
1302 output_line
= new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE
);
1303 output_button
= new QPushButton(tr("Browse..."));
1304 output_label
->setBuddy(output_line
);
1305 output_label
->setToolTip(
1306 tr("<b></b>The output file, which will be essentially identical"
1307 " to the input font but will contain new, generated hints."));
1308 output_line
->setCompleter(completer
);
1310 control_label
= new QLabel(tr("Control Instructions Fil&e:"));
1311 control_line
= new Drag_Drop_Line_Edit(DRAG_DROP_ANY
);
1312 control_button
= new QPushButton(tr("Browse..."));
1313 control_label
->setBuddy(control_line
);
1314 // we use the non-breaking hyphen U+2011 (‑) where necessary
1315 QString tooltip_string
=
1316 tr("<p>An optional control instructions file to tweak hinting"
1317 " and to override glyph assignments to styles."
1318 " This text file contains entries"
1319 " of one of the following syntax forms"
1320 " (with brackets indicating optional elements).<br>"
1323 " [ <i>subfont‑idx</i> ]"
1324 " <i>script</i>"
1325 " <i>feature</i>"
1326 " <tt>@</tt> <i>glyph‑ids</i><br>"
1328 " [ <i>subfont‑idx</i> ]"
1329 " <i>glyph‑id</i>"
1330 " <tt>left</tt> | <tt>right</tt> <i>points</i>"
1331 " [ <tt>(</tt><i>left‑offset</i><tt>,"
1332 "</tt><i>right‑offset</i><tt>)</tt> ]<br>"
1334 " [ <i>subfont‑idx</i> ]"
1335 " <i>glyph‑id</i>"
1336 " <tt>nodir</tt> <i>points</i><br>"
1338 " [ <i>subfont‑idx</i> ]"
1339 " <i>glyph‑id</i>"
1340 " <tt>touch</tt> | <tt>point</tt> <i>points</i>"
1341 " [ <tt>xshift</tt> <i>shift</i> ]"
1342 " [ <tt>yshift</tt> <i>shift</i> ]"
1343 " <tt>@</tt> <i>ppems</i><br>"
1346 "<i>subfont‑idx</i> gives the subfont index in a TTC,"
1347 " <i>glyph‑id</i> is a glyph name or index.<br>"
1350 "<i>script</i> and <i>feature</i> are four-letter tags"
1351 " that define a style the <i>glyph‑ids</i> are assigned to."
1352 " <i>glyph‑ids</i> is a comma-separated list of"
1353 " <i>glyph‑id</i> values and value ranges.<br>");
1355 tooltip_string
+= tr("Possible values for <i>script</i> are ");
1356 const Tag_Names
* sn
= script_names
;
1359 tooltip_string
+= QString::fromLocal8Bit("<tt>%1</tt> (%2)")
1361 .arg(sn
->description
);
1364 tooltip_string
+= ", ";
1367 tooltip_string
+= ".<br>";
1372 tooltip_string
+= tr("Possible values for <i>feature</i> are ");
1373 const Tag_Names
* fn
= feature_names
;
1376 tooltip_string
+= QString::fromLocal8Bit("<tt>%1</tt> (%2)")
1378 .arg(fn
->description
);
1381 tooltip_string
+= ", ";
1384 tooltip_string
+= ".<br>";
1389 tooltip_string
+= " <br>";
1392 tr("<tt>left</tt> (<tt>right</tt>) creates one-point segments"
1393 " with direction left (right), possibly having a width (in font units)"
1394 " given by <i>left‑offset</i> and <i>right‑offset</i>"
1395 " relative to the corresponding points.<br>"
1398 "<tt>nodir</tt> removes points from horizontal segments,"
1399 " making them <i>weak</i> points.<br>"
1402 "<tt>touch </tt> (<tt>point</tt>) defines delta exceptions"
1403 " to be applied before (after) the final"
1404 " <tt>IUP</tt> bytecode instructions."
1405 " <tt>touch</tt> also touches points, making them <i>strong</i>."
1406 " In ClearType mode, <tt>point</tt> and <tt>xshift</tt>"
1407 " have no effect.<br>"
1410 "x and y <i>shift</i> values are in the range [-1;1],"
1411 " rounded to multiples of 1/8px.<br>"
1413 "<i>points</i> and <i>ppems</i> are ranges for point indices"
1414 " and ppem values as with x height snapping exceptions.<br>"
1416 "Keywords <tt>left</tt>, <tt>right</tt>, <tt>nodir</tt>,"
1417 " <tt>point</tt>, <tt>touch</tt>, <tt>xshift</tt>, and"
1418 " <tt>yshift</tt> can be abbreviated as <tt>l</tt>, <tt>r</tt>,"
1419 " <tt>n</tt>, <tt>p</tt>, <tt>t</tt>, <tt>x</tt>, and <tt>y</tt>,"
1420 " respectively.<br>"
1422 "Control instruction entries are separated"
1423 " by character <tt>;</tt> or by a newline.<br>"
1425 "A trailing character <tt>\\</tt> continues the current line"
1426 " on the next line.<br>"
1428 "<tt>#</tt> starts a line comment, which gets ignored."
1429 " Empty lines are ignored, too.</p>"
1432 " <tt>cyrl sups @ one.sups-nine.sups, zero.sups</tt><br>"
1433 " <tt>Q left 38 (-70,20)</tt><br>"
1434 " <tt>Adieresis touch 3-6 yshift 0.25 @ 13</tt>");
1436 control_label
->setToolTip(tooltip_string
);
1437 control_line
->setCompleter(completer
);
1442 min_label
= new QLabel(tr("Hint Set Range Mi&nimum:"));
1443 min_box
= new QSpinBox
;
1444 min_label
->setBuddy(min_box
);
1445 min_label
->setToolTip(
1446 tr("The minimum PPEM value of the range for which"
1447 " <b>TTFautohint</b> computes <i>hint sets</i>."
1448 " A hint set for a given PPEM value hints this size optimally."
1449 " The larger the range, the more hint sets are considered,"
1450 " usually increasing the size of the bytecode.<br>"
1451 "Note that changing this range doesn't influence"
1452 " the <i>gasp</i> table:"
1453 " Hinting is enabled for all sizes."));
1454 min_box
->setKeyboardTracking(false);
1455 min_box
->setRange(2, 10000);
1457 max_label
= new QLabel(tr("Hint Set Range Ma&ximum:"));
1458 max_box
= new QSpinBox
;
1459 max_label
->setBuddy(max_box
);
1460 max_label
->setToolTip(
1461 tr("The maximum PPEM value of the range for which"
1462 " <b>TTFautohint</b> computes <i>hint sets</i>."
1463 " A hint set for a given PPEM value hints this size optimally."
1464 " The larger the range, the more hint sets are considered,"
1465 " usually increasing the size of the bytecode.<br>"
1466 "Note that changing this range doesn't influence"
1467 " the <i>gasp</i> table:"
1468 " Hinting is enabled for all sizes."));
1469 max_box
->setKeyboardTracking(false);
1470 max_box
->setRange(2, 10000);
1473 // OpenType default script
1475 default_label
= new QLabel(tr("Defa&ult Script:"));
1476 default_box
= new QComboBox
;
1477 default_label
->setBuddy(default_box
);
1478 default_label
->setToolTip(
1479 tr("This sets the default script for OpenType features:"
1480 " After applying all features that are handled specially"
1481 " (for example small caps or superscript glyphs),"
1482 " <b>TTFautohint</b> uses this value for the remaining features."));
1483 for (int i
= 0; script_names
[i
].tag
; i
++)
1485 // XXX: how to provide translations?
1486 default_box
->insertItem(i
,
1488 .arg(script_names
[i
].tag
)
1489 .arg(script_names
[i
].description
));
1493 // hinting and fallback controls
1495 fallback_label
= new QLabel(tr("Fallbac&k Script"));
1496 fallback_hint_or_scale_box
= new QComboBox
;
1497 fallback_box
= new QComboBox
;
1498 fallback_label
->setBuddy(fallback_hint_or_scale_box
);
1499 fallback_label
->setToolTip(
1500 tr("This sets the fallback script for glyphs"
1501 " that <b>TTFautohint</b> can't map to a script automatically."
1502 " Either hinting with the script's blue zone values"
1503 " or simple scaling with the script's scaling value"
1504 " can be selected."));
1505 fallback_hint_or_scale_box
->insertItem(fallback_do_scale
, tr("Scaling:"));
1506 fallback_hint_or_scale_box
->insertItem(fallback_do_hint
, tr("Hinting:"));
1507 for (int i
= 0; script_names
[i
].tag
; i
++)
1509 // XXX: how to provide translations?
1510 fallback_box
->insertItem(i
,
1512 .arg(script_names
[i
].tag
)
1513 .arg(script_names
[i
].description
));
1519 limit_label_text_with_key
= tr("Hinting &Limit:");
1520 limit_label_text
= tr("Hinting Limit:");
1521 limit_label
= new QLabel(limit_label_text_with_key
);
1522 limit_box
= new QSpinBox
;
1523 limit_label
->setBuddy(limit_box
);
1524 limit_label
->setToolTip(
1525 tr("Switch off hinting for PPEM values exceeding this limit."
1526 " Changing this value does not influence the size of the bytecode.<br>"
1527 "Note that <b>TTFautohint</b> handles this feature"
1528 " in the output font's bytecode and not in the <i>gasp</i> table."));
1529 limit_box
->setKeyboardTracking(false);
1530 limit_box
->setRange(2, 10000);
1532 no_limit_box_text_with_key
= tr("No Hinting &Limit");
1533 no_limit_box_text
= tr("No Hinting Limit");
1534 no_limit_box
= new QCheckBox(no_limit_box_text
);
1535 no_limit_box
->setToolTip(
1536 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
1537 " to the bytecode.<br>"
1538 "For testing only."));
1541 // x height increase limit
1543 increase_label_text_with_key
= tr("x Height In&crease Limit:");
1544 increase_label_text
= tr("x Height Increase Limit:");
1545 increase_label
= new QLabel(increase_label_text_with_key
);
1546 increase_box
= new QSpinBox
;
1547 increase_label
->setBuddy(increase_box
);
1548 increase_label
->setToolTip(
1549 tr("For PPEM values in the range 6 ≤ PPEM ≤ <i>n</i>,"
1550 " where <i>n</i> is the value selected by this spin box,"
1551 " round up the font's x height much more often than normally.<br>"
1552 "Use this if holes in letters like <i>e</i> get filled,"
1554 increase_box
->setKeyboardTracking(false);
1555 increase_box
->setRange(6, 10000);
1557 no_increase_box_text_with_key
= tr("No x Height In&crease");
1558 no_increase_box_text
= tr("No x Height Increase");
1559 no_increase_box
= new QCheckBox(no_increase_box_text
);
1560 no_increase_box
->setToolTip(
1561 tr("If switched on,"
1562 " <b>TTFautohint</b> does not increase the x height."));
1565 // x height snapping exceptions
1567 snapping_label
= new QLabel(tr("x Height Snapping Excep&tions:"));
1568 snapping_line
= new Tooltip_Line_Edit
;
1569 snapping_label
->setBuddy(snapping_line
);
1570 snapping_label
->setToolTip(
1571 tr("<p>A list of comma separated PPEM values or value ranges"
1572 " at which no x height snapping shall be applied"
1573 " (x height snapping usually slightly increases"
1574 " the size of all glyphs).</p>"
1577 " <tt>2, 3-5, 12-17</tt><br>"
1578 " <tt>-20, 40-</tt>"
1579 " (meaning PPEM ≤ 20 or PPEM ≥ 40)<br>"
1580 " <tt>-</tt> (meaning all possible PPEM values)"));
1585 family_suffix_label
= new QLabel(tr("Family Suffix&:"));
1586 family_suffix_line
= new Tooltip_Line_Edit
;
1587 family_suffix_label
->setBuddy(family_suffix_line
);
1588 family_suffix_label
->setToolTip(
1589 tr("A string that gets appended to the family name entries"
1590 " (name IDs 1, 4, 6, 16, and 21)"
1591 " in the font's <i>name</i> table.<br>"
1592 "Mainly for testing purposes, enabling the operating system"
1593 " to display fonts simultaneously that are hinted"
1594 " with different <b>TTFautohint</b> parameters."));
1597 // fallback stem width
1599 stem_width_label_text_with_key
= tr("Fallback &Stem Width:");
1600 stem_width_label_text
= tr("Fallback Stem Width:");
1601 stem_width_label
= new QLabel(stem_width_label_text_with_key
);
1602 stem_width_box
= new QSpinBox
;
1603 stem_width_label
->setBuddy(stem_width_box
);
1604 stem_width_label
->setToolTip(
1605 tr("Set horizontal stem width (in font units) for all scripts"
1606 " that lack proper standard characters in the font.<br>"
1607 "If not set, <b>TTFautohint</b> uses a hard-coded default value."));
1608 stem_width_box
->setKeyboardTracking(false);
1609 stem_width_box
->setRange(1, 10000);
1611 default_stem_width_box_text_with_key
= tr("Default Fallback &Stem Width");
1612 default_stem_width_box_text
= tr("Default Fallback Stem Width");
1613 default_stem_width_box
= new QCheckBox(default_stem_width_box_text
);
1614 default_stem_width_box
->setToolTip(
1615 tr("If switched on, <b>TTFautohint</b> uses a default value"
1616 " for the fallback stem width (50 font units at 2048 UPEM)."));
1621 wincomp_box
= new QCheckBox(tr("Windows Com&patibility"));
1622 wincomp_box
->setToolTip(
1623 tr("If switched on, add two artificial blue zones positioned at the"
1624 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1625 " (from the font's <i>OS/2</i> table)."
1626 " This option, usually in combination"
1627 " with value <tt>-</tt> (a single dash)"
1628 " for the <i>x Height Snapping Exceptions</i> option,"
1629 " should be used if those two <i>OS/2</i> values are tight,"
1630 " and you are experiencing clipping during rendering."));
1632 adjust_box
= new QCheckBox(tr("Ad&just Subglyphs"));
1633 adjust_box
->setToolTip(
1634 tr("If switched on, the original bytecode of the input font"
1635 " gets applied (at EM size, usually 2048ppem)"
1636 " to derive the glyph outlines for <b>TTFautohint</b>.<br>"
1637 "Use this option only if subglyphs"
1638 " are incorrectly scaled and shifted.<br>"
1639 "Note that the original bytecode will always be discarded."));
1641 hint_box
= new QCheckBox(tr("Hint Co&mposites")
1642 + " "); // make label wider
1643 hint_box
->setToolTip(
1644 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1645 " as a whole, including subglyphs."
1646 " Otherwise, glyph components get hinted separately.<br>"
1647 "Deactivating this flag reduces the bytecode size enormously,"
1648 " however, it might yield worse results."));
1650 symbol_box
= new QCheckBox(tr("S&ymbol Font"));
1651 symbol_box
->setToolTip(
1652 tr("If switched on, <b>TTFautohint</b> accepts fonts"
1653 " that don't contain a single standard character"
1654 " for any of the supported scripts.<br>"
1655 "Use this for symbol or dingbat fonts, for example."));
1657 dehint_box
= new QCheckBox(tr("&Dehint"));
1658 dehint_box
->setToolTip(
1659 tr("If set, remove all hints from the font.<br>"
1660 "For testing only."));
1662 info_label
= new QLabel(tr("ttf&autohint Info:"));
1663 info_box
= new QComboBox
;
1664 info_label
->setBuddy(info_box
);
1665 info_label
->setToolTip(
1666 tr("Specify which information about <b>TTFautohint</b>"
1667 " and its calling parameters gets added to the version string(s)"
1668 " (name ID 5) in the <i>name</i> table."));
1669 info_box
->insertItem(0, tr("None"));
1670 info_box
->insertItem(1, tr("Version"));
1671 info_box
->insertItem(2, tr("Version and Parameters"));
1673 TTFA_box
= new QCheckBox(tr("Add TTFA Info Ta&ble"));
1674 TTFA_box
->setToolTip(
1675 tr("If switched on, an SFNT table called <i>TTFA</i>"
1676 " gets added to the output font,"
1677 " holding a dump of all parameters."
1678 " In particular, it lists all control instructions."));
1681 // stem width and positioning
1683 stem_label
= new QLabel(tr("Stron&g Stem Width and Positioning:"));
1684 stem_label
->setToolTip(
1685 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1686 " that can be selected for various hinting modes."
1688 "<p><i>strong</i> (checkbox set):"
1689 " Position horizontal stems and snap stem widths"
1690 " to integer pixel values. While making the output look crisper,"
1691 " outlines become more distorted.</p>"
1693 "<p><i>smooth</i> (checkbox not set):"
1694 " Use discrete values for horizontal stems and stem widths."
1695 " This only slightly increases the contrast"
1696 " but avoids larger outline distortion.</p>"));
1698 gray_box
= new QCheckBox(tr("Grayscale"));
1699 gray_box
->setToolTip(
1700 tr("<b></b>Grayscale rendering, no ClearType activated."));
1701 stem_label
->setBuddy(gray_box
);
1703 gdi_box
= new QCheckBox(tr("GDI ClearType"));
1704 gdi_box
->setToolTip(
1705 tr("GDI ClearType rendering,"
1706 " introduced in 2000 for Windows XP.<br>"
1707 "The rasterizer version (as returned by the"
1708 " GETINFO bytecode instruction) is in the range"
1709 " 36 ≤ version < 38, and ClearType is enabled.<br>"
1710 "Along the vertical axis, this mode behaves like B/W rendering."));
1712 dw_box
= new QCheckBox(tr("DW ClearType"));
1714 tr("DirectWrite ClearType rendering,"
1715 " introduced in 2008 for Windows Vista.<br>"
1716 "The rasterizer version (as returned by the"
1717 " GETINFO bytecode instruction) is ≥ 38,"
1718 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1719 "Smooth rendering along the vertical axis."));
1724 watch_box
= new QCheckBox(tr("&Watch Input Files"));
1725 watch_box
->setToolTip(
1726 tr("If switched on, <b>TTFautohint</b> automatically re-runs"
1727 " the hinting process as soon as an input file"
1728 " (either the font or the control instructions file) is modified.<br>"
1729 "Pressing the %1 button starts watching.<br>"
1730 "If an error occurs, watching stops and must be restarted"
1731 " with the %1 button.")
1732 .arg(QUOTE_STRING_LITERAL(tr("Run"))));
1734 run_button
= new QPushButton(" "
1736 + " "); // make label wider
1738 if (horizontal_layout
)
1739 create_horizontal_layout();
1741 create_vertical_layout();
1745 // XXX distances are specified in pixels,
1746 // making the layout dependent on the output device resolution
1748 Main_GUI::create_vertical_layout()
1751 QGridLayout
* file_layout
= new QGridLayout
;
1753 file_layout
->addWidget(input_label
, 0, 0, Qt::AlignRight
);
1754 file_layout
->addWidget(input_line
, 0, 1);
1755 file_layout
->addWidget(input_button
, 0, 2);
1757 file_layout
->setRowStretch(1, 1);
1759 file_layout
->addWidget(output_label
, 2, 0, Qt::AlignRight
);
1760 file_layout
->addWidget(output_line
, 2, 1);
1761 file_layout
->addWidget(output_button
, 2, 2);
1763 file_layout
->setRowStretch(3, 1);
1765 file_layout
->addWidget(control_label
, 4, 0, Qt::AlignRight
);
1766 file_layout
->addWidget(control_line
, 4, 1);
1767 file_layout
->addWidget(control_button
, 4, 2);
1770 QGridLayout
* run_layout
= new QGridLayout
;
1772 run_layout
->addWidget(watch_box
, 0, 1);
1773 run_layout
->addWidget(run_button
, 0, 3, Qt::AlignRight
);
1774 run_layout
->setColumnStretch(0, 1);
1775 run_layout
->setColumnStretch(2, 2);
1780 QGridLayout
* gui_layout
= new QGridLayout
;
1781 QFrame
* hline
= new QFrame
;
1782 hline
->setFrameShape(QFrame::HLine
);
1783 int row
= 0; // this counter simplifies inserting new items
1785 gui_layout
->setRowMinimumHeight(row
, 10); // XXX urgh, pixels...
1786 gui_layout
->setRowStretch(row
++, 1);
1788 gui_layout
->addLayout(file_layout
, row
, 0, row
, -1);
1789 gui_layout
->setRowStretch(row
++, 1);
1791 gui_layout
->addWidget(hline
, row
, 0, row
, -1);
1792 gui_layout
->setRowStretch(row
++, 1);
1794 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1795 gui_layout
->setRowStretch(row
++, 1);
1797 gui_layout
->addWidget(min_label
, row
, 0, Qt::AlignRight
);
1798 gui_layout
->addWidget(min_box
, row
++, 1, Qt::AlignLeft
);
1799 gui_layout
->addWidget(max_label
, row
, 0, Qt::AlignRight
);
1800 gui_layout
->addWidget(max_box
, row
++, 1, Qt::AlignLeft
);
1802 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1803 gui_layout
->setRowStretch(row
++, 1);
1805 gui_layout
->addWidget(default_label
, row
, 0, Qt::AlignRight
);
1806 gui_layout
->addWidget(default_box
, row
++, 1, Qt::AlignLeft
);
1808 QGridLayout
* fallback_layout
= new QGridLayout
;
1809 fallback_layout
->addWidget(fallback_label
, 0, 0, Qt::AlignRight
);
1810 fallback_layout
->addWidget(fallback_hint_or_scale_box
, 0, 1, Qt::AlignLeft
);
1812 gui_layout
->addLayout(fallback_layout
, row
++, 0, Qt::AlignRight
);
1813 gui_layout
->addWidget(fallback_box
, row
++, 1, Qt::AlignLeft
);
1815 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1816 gui_layout
->setRowStretch(row
++, 1);
1818 gui_layout
->addWidget(limit_label
, row
, 0, Qt::AlignRight
);
1819 gui_layout
->addWidget(limit_box
, row
++, 1, Qt::AlignLeft
);
1820 gui_layout
->addWidget(no_limit_box
, row
++, 1);
1822 gui_layout
->addWidget(increase_label
, row
, 0, Qt::AlignRight
);
1823 gui_layout
->addWidget(increase_box
, row
++, 1, Qt::AlignLeft
);
1824 gui_layout
->addWidget(no_increase_box
, row
++, 1);
1826 gui_layout
->addWidget(snapping_label
, row
, 0, Qt::AlignRight
);
1827 gui_layout
->addWidget(snapping_line
, row
++, 1, Qt::AlignLeft
);
1829 gui_layout
->addWidget(stem_width_label
, row
, 0, Qt::AlignRight
);
1830 gui_layout
->addWidget(stem_width_box
, row
++, 1, Qt::AlignLeft
);
1831 gui_layout
->addWidget(default_stem_width_box
, row
++, 1);
1833 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1834 gui_layout
->setRowStretch(row
++, 1);
1836 gui_layout
->addWidget(wincomp_box
, row
++, 1);
1837 gui_layout
->addWidget(adjust_box
, row
++, 1);
1838 gui_layout
->addWidget(hint_box
, row
++, 1);
1839 gui_layout
->addWidget(symbol_box
, row
++, 1);
1840 gui_layout
->addWidget(dehint_box
, row
++, 1);
1842 gui_layout
->addWidget(info_label
, row
, 0, Qt::AlignRight
);
1843 gui_layout
->addWidget(info_box
, row
++, 1, Qt::AlignLeft
);
1844 gui_layout
->addWidget(TTFA_box
, row
++, 1);
1845 gui_layout
->addWidget(family_suffix_label
, row
, 0, Qt::AlignRight
);
1846 gui_layout
->addWidget(family_suffix_line
, row
++, 1, Qt::AlignLeft
);
1848 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1849 gui_layout
->setRowStretch(row
++, 1);
1851 gui_layout
->addWidget(stem_label
, row
, 0, Qt::AlignRight
);
1852 gui_layout
->addWidget(gray_box
, row
++, 1);
1853 gui_layout
->addWidget(gdi_box
, row
++, 1);
1854 gui_layout
->addWidget(dw_box
, row
++, 1);
1856 gui_layout
->setRowMinimumHeight(row
, 30); // XXX urgh, pixels...
1857 gui_layout
->setRowStretch(row
++, 1);
1859 gui_layout
->addLayout(run_layout
, row
, 0, row
, -1);
1861 // create dummy widget to register layout
1862 QWidget
* main_widget
= new QWidget
;
1863 main_widget
->setLayout(gui_layout
);
1864 setCentralWidget(main_widget
);
1865 setWindowTitle("TTFautohint");
1869 // XXX distances are specified in pixels,
1870 // making the layout dependent on the output device resolution
1872 Main_GUI::create_horizontal_layout()
1875 QGridLayout
* file_layout
= new QGridLayout
;
1877 file_layout
->addWidget(input_label
, 0, 0, Qt::AlignRight
);
1878 file_layout
->addWidget(input_line
, 0, 1);
1879 file_layout
->addWidget(input_button
, 0, 2);
1881 file_layout
->setRowStretch(1, 1);
1883 file_layout
->addWidget(output_label
, 2, 0, Qt::AlignRight
);
1884 file_layout
->addWidget(output_line
, 2, 1);
1885 file_layout
->addWidget(output_button
, 2, 2);
1887 file_layout
->setRowStretch(3, 1);
1889 file_layout
->addWidget(control_label
, 4, 0, Qt::AlignRight
);
1890 file_layout
->addWidget(control_line
, 4, 1);
1891 file_layout
->addWidget(control_button
, 4, 2);
1894 QGridLayout
* run_layout
= new QGridLayout
;
1896 run_layout
->addWidget(watch_box
, 0, 1);
1897 run_layout
->addWidget(run_button
, 0, 3, Qt::AlignRight
);
1898 run_layout
->setColumnStretch(0, 2);
1899 run_layout
->setColumnStretch(2, 3);
1900 run_layout
->setColumnStretch(4, 1);
1905 QGridLayout
* gui_layout
= new QGridLayout
;
1906 QFrame
* hline
= new QFrame
;
1907 hline
->setFrameShape(QFrame::HLine
);
1908 int row
= 0; // this counter simplifies inserting new items
1911 gui_layout
->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1912 gui_layout
->setColumnStretch(0, 1);
1915 gui_layout
->setRowMinimumHeight(row
, 10); // XXX urgh, pixels...
1916 gui_layout
->setRowStretch(row
++, 1);
1918 gui_layout
->addLayout(file_layout
, row
, 0, row
, -1);
1919 gui_layout
->setRowStretch(row
++, 1);
1921 gui_layout
->addWidget(hline
, row
, 0, row
, -1);
1922 gui_layout
->setRowStretch(row
++, 1);
1924 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1925 gui_layout
->setRowStretch(row
++, 1);
1927 gui_layout
->addWidget(min_label
, row
, 1, Qt::AlignRight
);
1928 gui_layout
->addWidget(min_box
, row
++, 2, Qt::AlignLeft
);
1929 gui_layout
->addWidget(max_label
, row
, 1, Qt::AlignRight
);
1930 gui_layout
->addWidget(max_box
, row
++, 2, Qt::AlignLeft
);
1932 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1933 gui_layout
->setRowStretch(row
++, 1);
1935 gui_layout
->addWidget(default_label
, row
, 1, Qt::AlignRight
);
1936 gui_layout
->addWidget(default_box
, row
++, 2, Qt::AlignLeft
);
1938 QGridLayout
* fallback_layout
= new QGridLayout
;
1939 fallback_layout
->addWidget(fallback_label
, 0, 0, Qt::AlignRight
);
1940 fallback_layout
->addWidget(fallback_hint_or_scale_box
, 0, 1, Qt::AlignLeft
);
1942 gui_layout
->addLayout(fallback_layout
, row
, 1, Qt::AlignRight
);
1943 gui_layout
->addWidget(fallback_box
, row
++, 2, Qt::AlignLeft
);
1945 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1946 gui_layout
->setRowStretch(row
++, 1);
1948 gui_layout
->addWidget(limit_label
, row
, 1, Qt::AlignRight
);
1949 gui_layout
->addWidget(limit_box
, row
++, 2, Qt::AlignLeft
);
1950 gui_layout
->addWidget(no_limit_box
, row
++, 2);
1952 gui_layout
->addWidget(increase_label
, row
, 1, Qt::AlignRight
);
1953 gui_layout
->addWidget(increase_box
, row
++, 2, Qt::AlignLeft
);
1954 gui_layout
->addWidget(no_increase_box
, row
++, 2);
1956 gui_layout
->addWidget(snapping_label
, row
, 1, Qt::AlignRight
);
1957 gui_layout
->addWidget(snapping_line
, row
++, 2, Qt::AlignLeft
);
1959 gui_layout
->addWidget(stem_width_label
, row
, 1, Qt::AlignRight
);
1960 gui_layout
->addWidget(stem_width_box
, row
++, 2, Qt::AlignLeft
);
1961 gui_layout
->addWidget(default_stem_width_box
, row
++, 2);
1963 gui_layout
->setRowMinimumHeight(row
, 30); // XXX urgh, pixels...
1964 gui_layout
->setRowStretch(row
++, 1);
1966 gui_layout
->addLayout(run_layout
, row
, 0, row
, -1);
1969 gui_layout
->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1970 gui_layout
->setColumnStretch(3, 1);
1974 gui_layout
->addWidget(wincomp_box
, row
++, 4);
1975 gui_layout
->addWidget(adjust_box
, row
++, 4);
1976 gui_layout
->addWidget(hint_box
, row
++, 4);
1977 gui_layout
->addWidget(symbol_box
, row
++, 4);
1978 gui_layout
->addWidget(dehint_box
, row
++, 4);
1980 gui_layout
->addWidget(info_label
, row
, 3, Qt::AlignRight
);
1981 gui_layout
->addWidget(info_box
, row
++, 4, Qt::AlignLeft
);
1982 gui_layout
->addWidget(TTFA_box
, row
++, 4);
1983 gui_layout
->addWidget(family_suffix_label
, row
, 3, Qt::AlignRight
);
1984 gui_layout
->addWidget(family_suffix_line
, row
++, 4, Qt::AlignLeft
);
1986 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1987 gui_layout
->setRowStretch(row
++, 1);
1989 gui_layout
->addWidget(stem_label
, row
++, 4);
1991 QGridLayout
* stem_layout
= new QGridLayout
;
1992 stem_layout
->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1993 stem_layout
->addWidget(gray_box
, 0, 1);
1994 stem_layout
->addWidget(gdi_box
, 1, 1);
1995 stem_layout
->addWidget(dw_box
, 2, 1);
1997 gui_layout
->addLayout(stem_layout
, row
, 4, 3, 1);
2001 gui_layout
->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
2002 gui_layout
->setColumnStretch(5, 1);
2004 // create dummy widget to register layout
2005 QWidget
* main_widget
= new QWidget
;
2006 main_widget
->setLayout(gui_layout
);
2007 setCentralWidget(main_widget
);
2008 setWindowTitle("TTFautohint");
2013 Main_GUI::create_connections()
2015 connect(input_button
, SIGNAL(clicked()),
2016 SLOT(browse_input()));
2017 connect(output_button
, SIGNAL(clicked()),
2018 SLOT(browse_output()));
2019 connect(control_button
, SIGNAL(clicked()),
2020 SLOT(browse_control()));
2022 connect(input_line
, SIGNAL(textChanged(QString
)),
2024 connect(output_line
, SIGNAL(textChanged(QString
)),
2027 connect(input_line
, SIGNAL(editingFinished()),
2028 SLOT(absolute_input()));
2029 connect(output_line
, SIGNAL(editingFinished()),
2030 SLOT(absolute_output()));
2031 connect(control_line
, SIGNAL(editingFinished()),
2032 SLOT(absolute_control()));
2034 connect(min_box
, SIGNAL(valueChanged(int)),
2036 connect(max_box
, SIGNAL(valueChanged(int)),
2039 connect(limit_box
, SIGNAL(valueChanged(int)),
2040 SLOT(check_limit()));
2041 connect(no_limit_box
, SIGNAL(clicked()),
2042 SLOT(check_no_limit()));
2044 connect(no_increase_box
, SIGNAL(clicked()),
2045 SLOT(check_no_increase()));
2047 connect(snapping_line
, SIGNAL(editingFinished()),
2048 SLOT(check_number_set()));
2049 connect(snapping_line
, SIGNAL(textEdited(QString
)),
2050 SLOT(clear_status_bar()));
2052 connect(family_suffix_line
, SIGNAL(editingFinished()),
2053 SLOT(check_family_suffix()));
2054 connect(family_suffix_line
, SIGNAL(textEdited(QString
)),
2055 SLOT(clear_status_bar()));
2057 connect(default_stem_width_box
, SIGNAL(clicked()),
2058 SLOT(check_default_stem_width()));
2060 connect(dehint_box
, SIGNAL(clicked()),
2061 SLOT(check_dehint()));
2063 connect(file_watcher
, SIGNAL(fileChanged(const QString
&)),
2064 SLOT(start_timer()));
2065 connect(timer
, SIGNAL(timeout()),
2066 SLOT(watch_files()));
2068 connect(watch_box
, SIGNAL(clicked()),
2069 SLOT(check_watch()));
2071 connect(run_button
, SIGNAL(clicked()),
2077 Main_GUI::create_actions()
2079 exit_act
= new QAction(tr("E&xit"), this);
2080 exit_act
->setShortcuts(QKeySequence::Quit
);
2081 connect(exit_act
, SIGNAL(triggered()), SLOT(close()));
2083 about_act
= new QAction(tr("&About"), this);
2084 connect(about_act
, SIGNAL(triggered()), SLOT(about()));
2086 about_Qt_act
= new QAction(tr("About &Qt"), this);
2087 connect(about_Qt_act
, SIGNAL(triggered()), qApp
, SLOT(aboutQt()));
2092 Main_GUI::create_menus()
2094 file_menu
= menuBar()->addMenu(tr("&File"));
2095 file_menu
->addAction(exit_act
);
2097 help_menu
= menuBar()->addMenu(tr("&Help"));
2098 help_menu
->addAction(about_act
);
2099 help_menu
->addAction(about_Qt_act
);
2104 Main_GUI::create_status_bar()
2106 statusBar()->showMessage("");
2111 Main_GUI::set_defaults()
2113 min_box
->setValue(hinting_range_min
);
2114 max_box
->setValue(hinting_range_max
);
2116 default_box
->setCurrentIndex(default_script_idx
);
2117 fallback_hint_or_scale_box
->setCurrentIndex(fallback_scaling
);
2118 fallback_box
->setCurrentIndex(fallback_script_idx
);
2120 limit_box
->setValue(hinting_limit
? hinting_limit
: hinting_range_max
);
2121 // handle command line option `--hinting-limit=0'
2124 hinting_limit
= max_box
->value();
2125 no_limit_box
->setChecked(true);
2128 increase_box
->setValue(increase_x_height
? increase_x_height
2129 : TA_INCREASE_X_HEIGHT
);
2130 // handle command line option `--increase-x-height=0'
2131 if (!increase_x_height
)
2133 increase_x_height
= TA_INCREASE_X_HEIGHT
;
2134 no_increase_box
->setChecked(true);
2137 snapping_line
->setText(x_height_snapping_exceptions_string
);
2138 family_suffix_line
->setText(family_suffix
);
2140 if (fallback_stem_width
)
2141 stem_width_box
->setValue(fallback_stem_width
);
2144 stem_width_box
->setValue(50);
2145 default_stem_width_box
->setChecked(true);
2148 if (windows_compatibility
)
2149 wincomp_box
->setChecked(true);
2150 if (adjust_subglyphs
)
2151 adjust_box
->setChecked(true);
2152 if (hint_composites
)
2153 hint_box
->setChecked(true);
2155 symbol_box
->setChecked(true);
2157 dehint_box
->setChecked(true);
2159 info_box
->setCurrentIndex(0);
2160 else if (detailed_info
)
2161 info_box
->setCurrentIndex(2);
2163 info_box
->setCurrentIndex(1);
2165 TTFA_box
->setChecked(true);
2167 if (gray_strong_stem_width
)
2168 gray_box
->setChecked(true);
2169 if (gdi_cleartype_strong_stem_width
)
2170 gdi_box
->setChecked(true);
2171 if (dw_cleartype_strong_stem_width
)
2172 dw_box
->setChecked(true);
2174 run_button
->setEnabled(false);
2181 check_no_increase();
2186 // do this last since it disables almost everything
2192 Main_GUI::read_settings()
2195 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
2196 // QSize size = settings.value("size", QSize(400, 400)).toSize();
2203 Main_GUI::write_settings()
2206 // settings.setValue("pos", pos());
2207 // settings.setValue("size", size());
2210 // end of maingui.cpp