3 // Copyright (C) 2012-2014 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.
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)
35 # define QUOTE_STRING(x) "\"" + x + "\""
36 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
40 // the available script tags and its descriptions are directly extracted
41 // from `ttfautohint-scripts.h'
42 typedef struct Script_Names_
45 const char* description
;
49 #define SCRIPT(s, S, d) \
52 const Script_Names script_names
[] =
54 #include <ttfautohint-scripts.h>
59 Main_GUI::Main_GUI(bool horizontal_layout
,
67 const char* exceptions
,
76 : hinting_range_min(range_min
),
77 hinting_range_max(range_max
),
79 gray_strong_stem_width(gray
),
80 gdi_cleartype_strong_stem_width(gdi
),
81 dw_cleartype_strong_stem_width(dw
),
82 increase_x_height(increase
),
83 x_height_snapping_exceptions_string(exceptions
),
84 ignore_restrictions(ignore
),
85 windows_compatibility(wincomp
),
87 hint_composites(composites
),
93 int none_script_idx
= 0;
95 // map fallback script tag to an index,
96 // replacing an invalid one with the default value
97 for (i
= 0; script_names
[i
].tag
; i
++)
99 if (!strcmp("none", script_names
[i
].tag
))
101 if (!strcmp(fallback
, script_names
[i
].tag
))
104 fallback_script_idx
= script_names
[i
].tag
? i
: none_script_idx
;
106 x_height_snapping_exceptions
= NULL
;
108 create_layout(horizontal_layout
);
109 create_connections();
117 setUnifiedTitleAndToolBarOnMac(true);
119 // XXX register translations somewhere and loop over them
120 if (QLocale::system().name() == "en_US")
121 locale
= new QLocale
;
123 locale
= new QLocale(QLocale::C
);
127 Main_GUI::~Main_GUI()
129 number_set_free(x_height_snapping_exceptions
);
136 Main_GUI::closeEvent(QCloseEvent
* event
)
146 QMessageBox::about(this,
147 tr("About TTFautohint"),
148 tr("<p>This is <b>TTFautohint</b> version %1<br>"
149 " Copyright %2 2011-2013<br>"
150 " by Werner Lemberg <tt><wl@gnu.org></tt></p>"
152 "<p><b>TTFautohint</b> adds new auto-generated hints"
153 " to a TrueType font or TrueType collection.</p>"
156 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
157 " License (FTL)</a> or"
158 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
166 Main_GUI::browse_input()
168 // XXX remember last directory
169 QString file
= QFileDialog::getOpenFileName(
171 tr("Open Input File"),
175 input_line
->setText(QDir::toNativeSeparators(file
));
180 Main_GUI::browse_output()
182 // XXX remember last directory
183 QString file
= QFileDialog::getSaveFileName(
185 tr("Open Output File"),
190 output_line
->setText(QDir::toNativeSeparators(file
));
195 Main_GUI::check_min()
197 int min
= min_box
->value();
198 int max
= max_box
->value();
199 int limit
= limit_box
->value();
201 max_box
->setValue(min
);
203 limit_box
->setValue(min
);
208 Main_GUI::check_max()
210 int min
= min_box
->value();
211 int max
= max_box
->value();
212 int limit
= limit_box
->value();
214 min_box
->setValue(max
);
216 limit_box
->setValue(max
);
221 Main_GUI::check_limit()
223 int min
= min_box
->value();
224 int max
= max_box
->value();
225 int limit
= limit_box
->value();
227 max_box
->setValue(limit
);
229 min_box
->setValue(limit
);
234 Main_GUI::check_dehint()
236 if (dehint_box
->isChecked())
238 min_label
->setEnabled(false);
239 min_box
->setEnabled(false);
241 max_label
->setEnabled(false);
242 max_box
->setEnabled(false);
244 fallback_label
->setEnabled(false);
245 fallback_box
->setEnabled(false);
247 no_limit_box
->setEnabled(false);
248 limit_label
->setEnabled(false);
249 limit_box
->setEnabled(false);
251 no_increase_box
->setEnabled(false);
252 increase_label
->setEnabled(false);
253 increase_box
->setEnabled(false);
255 snapping_label
->setEnabled(false);
256 snapping_line
->setEnabled(false);
258 wincomp_box
->setEnabled(false);
259 pre_box
->setEnabled(false);
260 hint_box
->setEnabled(false);
261 symbol_box
->setEnabled(false);
263 stem_label
->setEnabled(false);
264 gray_box
->setEnabled(false);
265 gdi_box
->setEnabled(false);
266 dw_box
->setEnabled(false);
270 min_label
->setEnabled(true);
271 min_box
->setEnabled(true);
273 max_label
->setEnabled(true);
274 max_box
->setEnabled(true);
276 fallback_label
->setEnabled(true);
277 fallback_box
->setEnabled(true);
279 no_limit_box
->setEnabled(true);
282 no_increase_box
->setEnabled(true);
285 snapping_label
->setEnabled(true);
286 snapping_line
->setEnabled(true);
288 wincomp_box
->setEnabled(true);
289 pre_box
->setEnabled(true);
290 hint_box
->setEnabled(true);
291 symbol_box
->setEnabled(true);
293 stem_label
->setEnabled(true);
294 gray_box
->setEnabled(true);
295 gdi_box
->setEnabled(true);
296 dw_box
->setEnabled(true);
302 Main_GUI::check_no_limit()
304 if (no_limit_box
->isChecked())
306 limit_label
->setEnabled(false);
307 limit_box
->setEnabled(false);
311 limit_label
->setEnabled(true);
312 limit_box
->setEnabled(true);
318 Main_GUI::check_no_increase()
320 if (no_increase_box
->isChecked())
322 increase_label
->setEnabled(false);
323 increase_box
->setEnabled(false);
327 increase_label
->setEnabled(true);
328 increase_box
->setEnabled(true);
334 Main_GUI::check_run()
336 if (input_line
->text().isEmpty() || output_line
->text().isEmpty())
337 run_button
->setEnabled(false);
339 run_button
->setEnabled(true);
344 Main_GUI::absolute_input()
346 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
347 if (!input_name
.isEmpty()
348 && QDir::isRelativePath(input_name
))
350 QDir
cur_path(QDir::currentPath() + "/" + input_name
);
351 input_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
357 Main_GUI::absolute_output()
359 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
360 if (!output_name
.isEmpty()
361 && QDir::isRelativePath(output_name
))
363 QDir
cur_path(QDir::currentPath() + "/" + output_name
);
364 output_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
370 Main_GUI::check_number_set()
372 QString text
= snapping_line
->text();
375 // construct ASCII string from arbitrary Unicode data;
376 // the idea is to accept, say, CJK fullwidth digits also
377 for (int i
= 0; i
< text
.size(); i
++)
379 QChar c
= text
.at(i
);
381 int digit
= c
.digitValue();
383 qs
+= QString::number(digit
);
384 else if (c
.isSpace())
386 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
387 // to the `-' key in some Japanese input methods
388 else if (c
.category() == QChar::Punctuation_Dash
389 || c
== QChar(0x30FC))
391 // various Unicode COMMA characters,
392 // including representation forms
393 else if (c
== QChar(',')
394 || c
== QChar(0x055D)
395 || c
== QChar(0x060C)
396 || c
== QChar(0x07F8)
397 || c
== QChar(0x1363)
398 || c
== QChar(0x1802)
399 || c
== QChar(0x1808)
400 || c
== QChar(0x3001)
401 || c
== QChar(0xA4FE)
402 || c
== QChar(0xA60D)
403 || c
== QChar(0xA6F5)
404 || c
== QChar(0xFE10)
405 || c
== QChar(0xFE11)
406 || c
== QChar(0xFE50)
407 || c
== QChar(0xFE51)
408 || c
== QChar(0xFF0C)
409 || c
== QChar(0xFF64))
412 qs
+= c
; // we do error handling below
415 if (x_height_snapping_exceptions
)
416 number_set_free(x_height_snapping_exceptions
);
418 QByteArray str
= qs
.toLocal8Bit();
419 const char* s
= number_set_parse(str
.constData(),
420 &x_height_snapping_exceptions
,
424 statusBar()->setStyleSheet("color: red;");
425 if (x_height_snapping_exceptions
== NUMBERSET_ALLOCATION_ERROR
)
426 statusBar()->showMessage(
427 tr("allocation error"));
428 else if (x_height_snapping_exceptions
== NUMBERSET_INVALID_CHARACTER
)
429 statusBar()->showMessage(
430 tr("invalid character (use digits, dashes, commas, and spaces)"));
431 else if (x_height_snapping_exceptions
== NUMBERSET_OVERFLOW
)
432 statusBar()->showMessage(
434 else if (x_height_snapping_exceptions
== NUMBERSET_INVALID_RANGE
)
435 statusBar()->showMessage(
436 tr("invalid range (minimum is 6, maximum is 32767)"));
437 else if (x_height_snapping_exceptions
== NUMBERSET_OVERLAPPING_RANGES
)
438 statusBar()->showMessage(
439 tr("overlapping ranges"));
440 else if (x_height_snapping_exceptions
== NUMBERSET_NOT_ASCENDING
)
441 statusBar()->showMessage(
442 tr("values und ranges must be specified in ascending order"));
444 snapping_line
->setText(qs
);
445 snapping_line
->setFocus(Qt::OtherFocusReason
);
446 snapping_line
->setCursorPosition(s
- str
.constData());
448 x_height_snapping_exceptions
= NULL
;
452 // normalize if there is no error
453 char* new_str
= number_set_show(x_height_snapping_exceptions
,
455 snapping_line
->setText(new_str
);
462 Main_GUI::clear_status_bar()
464 statusBar()->clearMessage();
465 statusBar()->setStyleSheet("");
470 Main_GUI::check_filenames(const QString
& input_name
,
471 const QString
& output_name
)
473 if (!QFile::exists(input_name
))
475 QMessageBox::warning(
478 tr("The file %1 cannot be found.")
479 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
))),
485 if (input_name
== output_name
)
487 QMessageBox::warning(
490 tr("Input and output file names must be different."),
496 if (QFile::exists(output_name
))
498 int ret
= QMessageBox::warning(
501 tr("The file %1 already exists.\n"
503 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
))),
504 QMessageBox::Yes
| QMessageBox::No
,
506 if (ret
== QMessageBox::No
)
515 Main_GUI::open_files(const QString
& input_name
,
517 const QString
& output_name
,
520 const int buf_len
= 1024;
523 *in
= fopen(qPrintable(input_name
), "rb");
526 strerror_r(errno
, buf
, buf_len
);
527 QMessageBox::warning(
530 tr("The following error occurred while opening input file %1:\n")
531 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
)))
532 + QString::fromLocal8Bit(buf
),
538 *out
= fopen(qPrintable(output_name
), "wb");
541 strerror_r(errno
, buf
, buf_len
);
542 QMessageBox::warning(
545 tr("The following error occurred while opening output file %1:\n")
546 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
547 + QString::fromLocal8Bit(buf
),
559 struct GUI_Progress_Data
563 QProgressDialog
* dialog
;
568 gui_progress(long curr_idx
,
574 GUI_Progress_Data
* data
= static_cast<GUI_Progress_Data
*>(user
);
576 if (num_sfnts
> 1 && curr_sfnt
!= data
->last_sfnt
)
578 data
->dialog
->setLabelText(QCoreApplication::translate(
580 "Auto-hinting subfont %1 of %2"
581 " with %3 glyphs...")
586 if (curr_sfnt
+ 1 == num_sfnts
)
588 data
->dialog
->setAutoReset(true);
589 data
->dialog
->setAutoClose(true);
593 data
->dialog
->setAutoReset(false);
594 data
->dialog
->setAutoClose(false);
597 data
->last_sfnt
= curr_sfnt
;
604 data
->dialog
->setLabelText(QCoreApplication::translate(
606 "Auto-hinting %1 glyphs...")
608 data
->dialog
->setMaximum(num_glyphs
- 1);
613 data
->dialog
->setValue(curr_idx
);
615 if (data
->dialog
->wasCanceled())
624 // return value 1 indicates a retry
627 Main_GUI::handle_error(TA_Error error
,
628 const unsigned char* error_string
,
633 if (error
== TA_Err_Canceled
)
635 else if (error
== TA_Err_Invalid_FreeType_Version
)
636 QMessageBox::critical(
639 tr("FreeType version 2.4.5 or higher is needed.\n"
640 "Are you perhaps using a wrong FreeType DLL?"),
643 else if (error
== TA_Err_Invalid_Font_Type
)
644 QMessageBox::warning(
647 tr("This font is not a valid font"
648 " in SFNT format with TrueType outlines.\n"
649 "In particular, CFF outlines are not supported."),
652 else if (error
== TA_Err_Already_Processed
)
653 QMessageBox::warning(
656 tr("This font has already been processed by TTFautohint."),
659 else if (error
== TA_Err_Missing_Legal_Permission
)
661 int yesno
= QMessageBox::warning(
664 tr("Bit 1 in the %1 field of the %2 table is set:"
665 " This font must not be modified"
666 " without permission of the legal owner.\n"
667 "Do you have such a permission?")
668 .arg(QUOTE_STRING_LITERAL("fsType"))
669 .arg(QUOTE_STRING_LITERAL("OS/2")),
670 QMessageBox::Yes
| QMessageBox::No
,
672 if (yesno
== QMessageBox::Yes
)
674 ignore_restrictions
= true;
678 else if (error
== TA_Err_Missing_Unicode_CMap
)
679 QMessageBox::warning(
682 tr("No Unicode character map."),
685 else if (error
== TA_Err_Missing_Symbol_CMap
)
686 QMessageBox::warning(
689 tr("No symbol character map."),
692 else if (error
== TA_Err_Missing_Glyph
)
693 QMessageBox::warning(
696 tr("No glyph for the key character"
697 " to derive standard stem width and height.\n"
698 "For the latin script, this key character is %1 (U+006F).\n"
700 "Set the %2 checkbox if you want to circumvent this test.")
701 .arg(QUOTE_STRING_LITERAL("o"))
702 .arg(QUOTE_STRING_LITERAL("symbol")),
706 QMessageBox::warning(
709 tr("Error code 0x%1 while autohinting font:\n")
710 .arg(error
, 2, 16, QLatin1Char('0'))
711 + QString::fromLocal8Bit((const char*)error_string
),
715 if (QFile::exists(output_name
) && remove(qPrintable(output_name
)))
717 const int buf_len
= 1024;
720 strerror_r(errno
, buf
, buf_len
);
721 QMessageBox::warning(
724 tr("The following error occurred while removing output file %1:\n")
725 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
726 + QString::fromLocal8Bit(buf
),
738 statusBar()->clearMessage();
740 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
741 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
742 if (!check_filenames(input_name
, output_name
))
745 // we need C file descriptors for communication with TTF_autohint
750 if (!open_files(input_name
, &input
, output_name
, &output
))
753 QProgressDialog dialog
;
754 dialog
.setCancelButtonText(tr("Cancel"));
755 dialog
.setMinimumDuration(1000);
756 dialog
.setWindowModality(Qt::WindowModal
);
758 const unsigned char* error_string
;
759 TA_Info_Func info_func
= info
;
760 GUI_Progress_Data gui_progress_data
= {-1, true, &dialog
};
763 info_data
.data
= NULL
; // must be deallocated after use
764 info_data
.data_wide
= NULL
; // must be deallocated after use
765 info_data
.data_len
= 0;
766 info_data
.data_wide_len
= 0;
768 info_data
.hinting_range_min
= min_box
->value();
769 info_data
.hinting_range_max
= max_box
->value();
770 info_data
.hinting_limit
= no_limit_box
->isChecked()
772 : limit_box
->value();
774 info_data
.gray_strong_stem_width
= gray_box
->isChecked();
775 info_data
.gdi_cleartype_strong_stem_width
= gdi_box
->isChecked();
776 info_data
.dw_cleartype_strong_stem_width
= dw_box
->isChecked();
778 info_data
.increase_x_height
= no_increase_box
->isChecked()
780 : increase_box
->value();
781 info_data
.x_height_snapping_exceptions
= x_height_snapping_exceptions
;
783 info_data
.windows_compatibility
= wincomp_box
->isChecked();
784 info_data
.pre_hinting
= pre_box
->isChecked();
785 info_data
.hint_composites
= hint_box
->isChecked();
786 info_data
.symbol
= symbol_box
->isChecked();
787 info_data
.dehint
= dehint_box
->isChecked();
789 strncpy(info_data
.fallback_script
,
790 script_names
[fallback_box
->currentIndex()].tag
,
791 sizeof (info_data
.fallback_script
));
793 if (info_box
->isChecked())
795 int ret
= build_version_string(&info_data
);
797 QMessageBox::information(
800 tr("Can't allocate memory for <b>TTFautohint</b> options string"
801 " in <i>name</i> table."),
805 QMessageBox::information(
808 tr("<b>TTFautohint</b> options string"
809 " in <i>name</i> table too long."),
816 QByteArray snapping_string
= snapping_line
->text().toLocal8Bit();
819 TTF_autohint("in-file, out-file,"
820 "hinting-range-min, hinting-range-max,"
822 "gray-strong-stem-width,"
823 "gdi-cleartype-strong-stem-width,"
824 "dw-cleartype-strong-stem-width,"
826 "progress-callback, progress-callback-data,"
827 "info-callback, info-callback-data,"
828 "ignore-restrictions,"
829 "windows-compatibility,"
833 "x-height-snapping-exceptions,"
834 "fallback-script, symbol,"
837 info_data
.hinting_range_min
, info_data
.hinting_range_max
,
838 info_data
.hinting_limit
,
839 info_data
.gray_strong_stem_width
,
840 info_data
.gdi_cleartype_strong_stem_width
,
841 info_data
.dw_cleartype_strong_stem_width
,
843 gui_progress
, &gui_progress_data
,
844 info_func
, &info_data
,
846 info_data
.windows_compatibility
,
847 info_data
.pre_hinting
,
848 info_data
.hint_composites
,
849 info_data
.increase_x_height
,
850 snapping_string
.constData(),
851 info_data
.fallback_script
, info_data
.symbol
,
854 if (info_box
->isChecked())
856 free(info_data
.data
);
857 free(info_data
.data_wide
);
865 if (handle_error(error
, error_string
, output_name
))
869 statusBar()->showMessage(tr("Auto-hinting finished."));
873 // XXX distances are specified in pixels,
874 // making the layout dependent on the output device resolution
876 Main_GUI::create_layout(bool horizontal_layout
)
881 QCompleter
* completer
= new QCompleter(this);
882 QFileSystemModel
* model
= new QFileSystemModel(completer
);
883 model
->setRootPath(QDir::rootPath());
884 completer
->setModel(model
);
886 input_label
= new QLabel(tr("&Input File:"));
887 input_line
= new Drag_Drop_Line_Edit
;
888 input_button
= new QPushButton(tr("Browse..."));
889 input_label
->setBuddy(input_line
);
890 // enforce rich text to get nice word wrapping
891 input_label
->setToolTip(
892 tr("<b></b>The input file, either a TrueType font (TTF),"
893 " TrueType collection (TTC), or a TrueType-based OpenType font."));
894 input_line
->setCompleter(completer
);
896 output_label
= new QLabel(tr("&Output File:"));
897 output_line
= new Drag_Drop_Line_Edit
;
898 output_button
= new QPushButton(tr("Browse..."));
899 output_label
->setBuddy(output_line
);
900 output_label
->setToolTip(
901 tr("<b></b>The output file, which will be essentially identical"
902 " to the input font but will contain new, generated hints."));
903 output_line
->setCompleter(completer
);
908 min_label
= new QLabel(tr("Hint Set Range Mi&nimum:"));
909 min_box
= new QSpinBox
;
910 min_label
->setBuddy(min_box
);
911 min_label
->setToolTip(
912 tr("The minimum PPEM value of the range for which"
913 " <b>TTFautohint</b> computes <i>hint sets</i>."
914 " A hint set for a given PPEM value hints this size optimally."
915 " The larger the range, the more hint sets are considered,"
916 " usually increasing the size of the bytecode.<br>"
917 "Note that changing this range doesn't influence"
918 " the <i>gasp</i> table:"
919 " Hinting is enabled for all sizes."));
920 min_box
->setKeyboardTracking(false);
921 min_box
->setRange(2, 10000);
923 max_label
= new QLabel(tr("Hint Set Range Ma&ximum:"));
924 max_box
= new QSpinBox
;
925 max_label
->setBuddy(max_box
);
926 max_label
->setToolTip(
927 tr("The maximum PPEM value of the range for which"
928 " <b>TTFautohint</b> computes <i>hint sets</i>."
929 " A hint set for a given PPEM value hints this size optimally."
930 " The larger the range, the more hint sets are considered,"
931 " usually increasing the size of the bytecode.<br>"
932 "Note that changing this range doesn't influence"
933 " the <i>gasp</i> table:"
934 " Hinting is enabled for all sizes."));
935 max_box
->setKeyboardTracking(false);
936 max_box
->setRange(2, 10000);
939 // hinting and fallback controls
941 fallback_label
= new QLabel(tr("Fallback &Script:"));
942 fallback_box
= new QComboBox
;
943 fallback_label
->setBuddy(fallback_box
);
944 fallback_label
->setToolTip(
945 tr("This sets the fallback script module for glyphs"
946 " that <b>TTFautohint</b> can't map to a script automatically."));
947 for (int i
= 0; script_names
[i
].tag
; i
++)
949 // XXX: how to provide translations?
950 fallback_box
->insertItem(i
,
952 .arg(script_names
[i
].tag
)
953 .arg(script_names
[i
].description
));
959 limit_label
= new QLabel(tr("Hinting &Limit:"));
960 limit_box
= new QSpinBox
;
961 limit_label
->setBuddy(limit_box
);
962 limit_label
->setToolTip(
963 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
964 " sizes larger than this PPEM value are not hinted"
965 " (regardless of the values in the <i>gasp</i> table)."));
966 limit_box
->setKeyboardTracking(false);
967 limit_box
->setRange(2, 10000);
969 no_limit_box
= new QCheckBox(tr("No Hinting Limit"), this);
970 no_limit_box
->setToolTip(
971 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
972 " to the bytecode."));
975 // x height increase limit
977 increase_label
= new QLabel(tr("x Height In&crease Limit:"));
978 increase_box
= new QSpinBox
;
979 increase_label
->setBuddy(increase_box
);
980 increase_label
->setToolTip(
981 tr("For PPEM values in the range 5 < PPEM < <i>n</i>,"
982 " where <i>n</i> is the value selected by this spin box,"
983 " round up the font's x height much more often than normally.<br>"
984 "Use this if holes in letters like <i>e</i> get filled,"
986 increase_box
->setKeyboardTracking(false);
987 increase_box
->setRange(6, 10000);
989 no_increase_box
= new QCheckBox(tr("No x Height Increase"), this);
990 no_increase_box
->setToolTip(
992 " <b>TTFautohint</b> does not increase the x height."));
995 // x height snapping exceptions
997 snapping_label
= new QLabel(tr("x Height Snapping Excep&tions:"));
998 snapping_line
= new Tooltip_Line_Edit
;
999 snapping_label
->setBuddy(snapping_line
);
1000 snapping_label
->setToolTip(
1001 tr("<p>A list of comma separated PPEM values or value ranges"
1002 " at which no x height snapping shall be applied"
1003 " (x height snapping usually slightly increases"
1004 " the size of all glyphs).</p>"
1007 " <tt>2, 3-5, 12-17</tt><br>"
1008 " <tt>-20, 40-</tt>"
1009 " (meaning PPEM ≤ 20 or PPEM ≥ 40)<br>"
1010 " <tt>-</tt> (meaning all possible PPEM values)"));
1015 wincomp_box
= new QCheckBox(tr("Windows Com&patibility"), this);
1016 wincomp_box
->setToolTip(
1017 tr("If switched on, add two artificial blue zones positioned at the"
1018 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1019 " (from the font's <i>OS/2</i> table)."
1020 " This option, usually in combination"
1021 " with value <tt>-</tt> (a single dash)"
1022 " for the <i>x Height Snapping Exceptions</i> option,"
1023 " should be used if those two <i>OS/2</i> values are tight,"
1024 " and you are experiencing clipping during rendering."));
1026 pre_box
= new QCheckBox(tr("Pr&e-hinting"), this);
1027 pre_box
->setToolTip(
1028 tr("If switched on, the original bytecode of the input font"
1029 " gets applied (at EM size, usually 2048ppem)"
1030 " to derive the glyph outlines for <b>TTFautohint</b>."
1031 " Note that the original bytecode will always be discarded."));
1033 hint_box
= new QCheckBox(tr("Hint Co&mposites")
1034 + " ", this); // make label wider
1035 hint_box
->setToolTip(
1036 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1037 " as a whole, including subglyphs."
1038 " Otherwise, glyph components get hinted separately.<br>"
1039 "Deactivating this flag reduces the bytecode size enormously,"
1040 " however, it might yield worse results."));
1042 symbol_box
= new QCheckBox(tr("S&ymbol Font"), this);
1043 symbol_box
->setToolTip(
1044 tr("If switched on, <b>TTFautohint</b> uses default values"
1045 " for standard stem width and height"
1046 " instead of deriving these values from the input font.<br>"
1047 "Use this for fonts that don't contain glyphs"
1048 " of a (supported) script."));
1050 dehint_box
= new QCheckBox(tr("&Dehint"), this);
1051 dehint_box
->setToolTip(
1052 tr("<b></b>If set, remove all hints from the font."));
1054 info_box
= new QCheckBox(tr("Add ttf&autohint Info"), this);
1055 info_box
->setToolTip(
1056 tr("If switched on, information about <b>TTFautohint</b>"
1057 " and its calling parameters are added to the version string(s)"
1058 " (name ID 5) in the <i>name</i> table."));
1061 // stem width and positioning
1063 stem_label
= new QLabel(tr("Strong Stem &Width and Positioning:"));
1064 stem_label
->setToolTip(
1065 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1066 " that can be selected for various hinting modes."
1068 "<p><i>strong</i> (checkbox set):"
1069 " Position horizontal stems and snap stem widths"
1070 " to integer pixel values. While making the output look crisper,"
1071 " outlines become more distorted.</p>"
1073 "<p><i>smooth</i> (checkbox not set):"
1074 " Use discrete values for horizontal stems and stem widths."
1075 " This only slightly increases the contrast"
1076 " but avoids larger outline distortion.</p>"));
1078 gray_box
= new QCheckBox(tr("Grayscale"), this);
1079 gray_box
->setToolTip(
1080 tr("<b></b>Grayscale rendering, no ClearType activated."));
1081 stem_label
->setBuddy(gray_box
);
1083 gdi_box
= new QCheckBox(tr("GDI ClearType"), this);
1084 gdi_box
->setToolTip(
1085 tr("GDI ClearType rendering,"
1086 " introduced in 2000 for Windows XP.<br>"
1087 "The rasterizer version (as returned by the"
1088 " GETINFO bytecode instruction) is in the range"
1089 " 36 ≤ version < 38, and ClearType is enabled.<br>"
1090 "Along the vertical axis, this mode behaves like B/W rendering."));
1092 dw_box
= new QCheckBox(tr("DW ClearType"), this);
1094 tr("DirectWrite ClearType rendering,"
1095 " introduced in 2008 for Windows Vista.<br>"
1096 "The rasterizer version (as returned by the"
1097 " GETINFO bytecode instruction) is ≥ 38,"
1098 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1099 "Smooth rendering along the vertical axis."));
1104 run_button
= new QPushButton(" "
1106 + " "); // make label wider
1108 if (horizontal_layout
)
1109 create_horizontal_layout();
1111 create_vertical_layout();
1115 // XXX distances are specified in pixels,
1116 // making the layout dependent on the output device resolution
1118 Main_GUI::create_vertical_layout()
1121 QGridLayout
* file_layout
= new QGridLayout
;
1123 file_layout
->addWidget(input_label
, 0, 0, Qt::AlignRight
);
1124 file_layout
->addWidget(input_line
, 0, 1);
1125 file_layout
->addWidget(input_button
, 0, 2);
1127 file_layout
->setRowStretch(1, 1);
1129 file_layout
->addWidget(output_label
, 2, 0, Qt::AlignRight
);
1130 file_layout
->addWidget(output_line
, 2, 1);
1131 file_layout
->addWidget(output_button
, 2, 2);
1136 QGridLayout
* gui_layout
= new QGridLayout
;
1137 QFrame
* hline
= new QFrame
;
1138 hline
->setFrameShape(QFrame::HLine
);
1139 int row
= 0; // this counter simplifies inserting new items
1141 gui_layout
->setRowMinimumHeight(row
, 10); // XXX urgh, pixels...
1142 gui_layout
->setRowStretch(row
++, 1);
1144 gui_layout
->addLayout(file_layout
, row
, 0, row
, -1);
1145 gui_layout
->setRowStretch(row
++, 1);
1147 gui_layout
->addWidget(hline
, row
, 0, row
, -1);
1148 gui_layout
->setRowStretch(row
++, 1);
1150 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1151 gui_layout
->setRowStretch(row
++, 1);
1153 gui_layout
->addWidget(min_label
, row
, 0, Qt::AlignRight
);
1154 gui_layout
->addWidget(min_box
, row
++, 1, Qt::AlignLeft
);
1155 gui_layout
->addWidget(max_label
, row
, 0, Qt::AlignRight
);
1156 gui_layout
->addWidget(max_box
, row
++, 1, Qt::AlignLeft
);
1158 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1159 gui_layout
->setRowStretch(row
++, 1);
1161 gui_layout
->addWidget(fallback_label
, row
, 0, Qt::AlignRight
);
1162 gui_layout
->addWidget(fallback_box
, row
++, 1, Qt::AlignLeft
);
1164 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1165 gui_layout
->setRowStretch(row
++, 1);
1167 gui_layout
->addWidget(limit_label
, row
, 0, Qt::AlignRight
);
1168 gui_layout
->addWidget(limit_box
, row
++, 1, Qt::AlignLeft
);
1169 gui_layout
->addWidget(no_limit_box
, row
++, 1);
1171 gui_layout
->addWidget(increase_label
, row
, 0, Qt::AlignRight
);
1172 gui_layout
->addWidget(increase_box
, row
++, 1, Qt::AlignLeft
);
1173 gui_layout
->addWidget(no_increase_box
, row
++, 1);
1175 gui_layout
->addWidget(snapping_label
, row
, 0, Qt::AlignRight
);
1176 gui_layout
->addWidget(snapping_line
, row
++, 1, Qt::AlignLeft
);
1178 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1179 gui_layout
->setRowStretch(row
++, 1);
1181 gui_layout
->addWidget(wincomp_box
, row
++, 1);
1182 gui_layout
->addWidget(pre_box
, row
++, 1);
1183 gui_layout
->addWidget(hint_box
, row
++, 1);
1184 gui_layout
->addWidget(symbol_box
, row
++, 1);
1185 gui_layout
->addWidget(dehint_box
, row
++, 1);
1186 gui_layout
->addWidget(info_box
, row
++, 1);
1188 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1189 gui_layout
->setRowStretch(row
++, 1);
1191 gui_layout
->addWidget(stem_label
, row
, 0, Qt::AlignRight
);
1192 gui_layout
->addWidget(gray_box
, row
++, 1);
1193 gui_layout
->addWidget(gdi_box
, row
++, 1);
1194 gui_layout
->addWidget(dw_box
, row
++, 1);
1196 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1197 gui_layout
->setRowStretch(row
++, 1);
1199 gui_layout
->addWidget(run_button
, row
++, 1, Qt::AlignRight
);
1201 // create dummy widget to register layout
1202 QWidget
* main_widget
= new QWidget
;
1203 main_widget
->setLayout(gui_layout
);
1204 setCentralWidget(main_widget
);
1205 setWindowTitle("TTFautohint");
1209 // XXX distances are specified in pixels,
1210 // making the layout dependent on the output device resolution
1212 Main_GUI::create_horizontal_layout()
1215 QGridLayout
* file_layout
= new QGridLayout
;
1217 file_layout
->addWidget(input_label
, 0, 0, Qt::AlignRight
);
1218 file_layout
->addWidget(input_line
, 0, 1);
1219 file_layout
->addWidget(input_button
, 0, 2);
1221 file_layout
->setRowStretch(1, 1);
1223 file_layout
->addWidget(output_label
, 2, 0, Qt::AlignRight
);
1224 file_layout
->addWidget(output_line
, 2, 1);
1225 file_layout
->addWidget(output_button
, 2, 2);
1227 QGridLayout
* gui_layout
= new QGridLayout
;
1228 QFrame
* hline
= new QFrame
;
1229 hline
->setFrameShape(QFrame::HLine
);
1230 int row
= 0; // this counter simplifies inserting new items
1233 gui_layout
->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1234 gui_layout
->setColumnStretch(0, 1);
1237 gui_layout
->setRowMinimumHeight(row
, 10); // XXX urgh, pixels...
1238 gui_layout
->setRowStretch(row
++, 1);
1240 gui_layout
->addLayout(file_layout
, row
, 0, row
, -1);
1241 gui_layout
->setRowStretch(row
++, 1);
1243 gui_layout
->addWidget(hline
, row
, 0, row
, -1);
1244 gui_layout
->setRowStretch(row
++, 1);
1246 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1247 gui_layout
->setRowStretch(row
++, 1);
1249 gui_layout
->addWidget(min_label
, row
, 1, Qt::AlignRight
);
1250 gui_layout
->addWidget(min_box
, row
++, 2, Qt::AlignLeft
);
1251 gui_layout
->addWidget(max_label
, row
, 1, Qt::AlignRight
);
1252 gui_layout
->addWidget(max_box
, row
++, 2, Qt::AlignLeft
);
1254 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1255 gui_layout
->setRowStretch(row
++, 1);
1257 gui_layout
->addWidget(fallback_label
, row
, 1, Qt::AlignRight
);
1258 gui_layout
->addWidget(fallback_box
, row
++, 2, Qt::AlignLeft
);
1260 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1261 gui_layout
->setRowStretch(row
++, 1);
1263 gui_layout
->addWidget(limit_label
, row
, 1, Qt::AlignRight
);
1264 gui_layout
->addWidget(limit_box
, row
++, 2, Qt::AlignLeft
);
1265 gui_layout
->addWidget(no_limit_box
, row
++, 2);
1267 gui_layout
->addWidget(increase_label
, row
, 1, Qt::AlignRight
);
1268 gui_layout
->addWidget(increase_box
, row
++, 2, Qt::AlignLeft
);
1269 gui_layout
->addWidget(no_increase_box
, row
++, 2);
1271 gui_layout
->addWidget(snapping_label
, row
, 1, Qt::AlignRight
);
1272 gui_layout
->addWidget(snapping_line
, row
++, 2, Qt::AlignLeft
);
1275 gui_layout
->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1276 gui_layout
->setColumnStretch(3, 1);
1280 gui_layout
->addWidget(wincomp_box
, row
++, 4);
1281 gui_layout
->addWidget(pre_box
, row
++, 4);
1282 gui_layout
->addWidget(hint_box
, row
++, 4);
1283 gui_layout
->addWidget(symbol_box
, row
++, 4);
1284 gui_layout
->addWidget(dehint_box
, row
++, 4);
1285 gui_layout
->addWidget(info_box
, row
++, 4);
1287 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1288 gui_layout
->setRowStretch(row
++, 1);
1290 gui_layout
->addWidget(stem_label
, row
++, 4);
1292 QGridLayout
* stem_layout
= new QGridLayout
;
1293 stem_layout
->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1294 stem_layout
->addWidget(gray_box
, 0, 1);
1295 stem_layout
->addWidget(gdi_box
, 1, 1);
1296 stem_layout
->addWidget(dw_box
, 2, 1);
1298 gui_layout
->addLayout(stem_layout
, row
, 4, 3, 1);
1301 gui_layout
->setRowMinimumHeight(row
, 20); // XXX urgh, pixels...
1302 gui_layout
->setRowStretch(row
++, 1);
1304 gui_layout
->addWidget(run_button
, row
++, 4, Qt::AlignRight
);
1307 gui_layout
->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1308 gui_layout
->setColumnStretch(5, 1);
1310 // create dummy widget to register layout
1311 QWidget
* main_widget
= new QWidget
;
1312 main_widget
->setLayout(gui_layout
);
1313 setCentralWidget(main_widget
);
1314 setWindowTitle("TTFautohint");
1319 Main_GUI::create_connections()
1321 connect(input_button
, SIGNAL(clicked()), this,
1322 SLOT(browse_input()));
1323 connect(output_button
, SIGNAL(clicked()), this,
1324 SLOT(browse_output()));
1326 connect(input_line
, SIGNAL(textChanged(QString
)), this,
1328 connect(output_line
, SIGNAL(textChanged(QString
)), this,
1331 connect(input_line
, SIGNAL(editingFinished()), this,
1332 SLOT(absolute_input()));
1333 connect(output_line
, SIGNAL(editingFinished()), this,
1334 SLOT(absolute_output()));
1336 connect(min_box
, SIGNAL(valueChanged(int)), this,
1338 connect(max_box
, SIGNAL(valueChanged(int)), this,
1341 connect(limit_box
, SIGNAL(valueChanged(int)), this,
1342 SLOT(check_limit()));
1343 connect(no_limit_box
, SIGNAL(clicked()), this,
1344 SLOT(check_no_limit()));
1346 connect(no_increase_box
, SIGNAL(clicked()), this,
1347 SLOT(check_no_increase()));
1349 connect(snapping_line
, SIGNAL(editingFinished()), this,
1350 SLOT(check_number_set()));
1351 connect(snapping_line
, SIGNAL(textEdited(QString
)), this,
1352 SLOT(clear_status_bar()));
1354 connect(dehint_box
, SIGNAL(clicked()), this,
1355 SLOT(check_dehint()));
1357 connect(run_button
, SIGNAL(clicked()), this,
1363 Main_GUI::create_actions()
1365 exit_act
= new QAction(tr("E&xit"), this);
1366 exit_act
->setShortcuts(QKeySequence::Quit
);
1367 connect(exit_act
, SIGNAL(triggered()), this, SLOT(close()));
1369 about_act
= new QAction(tr("&About"), this);
1370 connect(about_act
, SIGNAL(triggered()), this, SLOT(about()));
1372 about_Qt_act
= new QAction(tr("About &Qt"), this);
1373 connect(about_Qt_act
, SIGNAL(triggered()), qApp
, SLOT(aboutQt()));
1378 Main_GUI::create_menus()
1380 file_menu
= menuBar()->addMenu(tr("&File"));
1381 file_menu
->addAction(exit_act
);
1383 help_menu
= menuBar()->addMenu(tr("&Help"));
1384 help_menu
->addAction(about_act
);
1385 help_menu
->addAction(about_Qt_act
);
1390 Main_GUI::create_status_bar()
1392 statusBar()->showMessage("");
1397 Main_GUI::set_defaults()
1399 min_box
->setValue(hinting_range_min
);
1400 max_box
->setValue(hinting_range_max
);
1402 fallback_box
->setCurrentIndex(fallback_script_idx
);
1404 limit_box
->setValue(hinting_limit
? hinting_limit
: hinting_range_max
);
1405 // handle command line option `--hinting-limit=0'
1408 hinting_limit
= max_box
->value();
1409 no_limit_box
->setChecked(true);
1412 increase_box
->setValue(increase_x_height
? increase_x_height
1413 : TA_INCREASE_X_HEIGHT
);
1414 // handle command line option `--increase-x-height=0'
1415 if (!increase_x_height
)
1417 increase_x_height
= TA_INCREASE_X_HEIGHT
;
1418 no_increase_box
->setChecked(true);
1421 snapping_line
->setText(x_height_snapping_exceptions_string
);
1423 if (windows_compatibility
)
1424 wincomp_box
->setChecked(true);
1426 pre_box
->setChecked(true);
1427 if (hint_composites
)
1428 hint_box
->setChecked(true);
1430 symbol_box
->setChecked(true);
1432 dehint_box
->setChecked(true);
1434 info_box
->setChecked(true);
1436 if (gray_strong_stem_width
)
1437 gray_box
->setChecked(true);
1438 if (gdi_cleartype_strong_stem_width
)
1439 gdi_box
->setChecked(true);
1440 if (dw_cleartype_strong_stem_width
)
1441 dw_box
->setChecked(true);
1443 run_button
->setEnabled(false);
1450 check_no_increase();
1453 // do this last since it disables almost everything
1459 Main_GUI::read_settings()
1462 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1463 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1470 Main_GUI::write_settings()
1473 // settings.setValue("pos", pos());
1474 // settings.setValue("size", size());
1477 // end of maingui.cpp