3 // Copyright (C) 2012 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 Main_GUI::Main_GUI(int range_min
,
49 : hinting_range_min(range_min
),
50 hinting_range_max(range_max
),
52 ignore_permissions(ignore
),
54 increase_x_height(increase
),
56 latin_fallback(fallback
),
67 setUnifiedTitleAndToolBarOnMac(true);
69 // XXX register translations somewhere and loop over them
70 if (QLocale::system().name() == "en_US")
73 locale
= new QLocale(QLocale::C
);
80 Main_GUI::closeEvent(QCloseEvent
* event
)
90 QMessageBox::about(this,
91 tr("About TTFautohint"),
92 tr("<p>This is <b>TTFautohint</b> version %1<br>"
93 " Copyright %2 2011-2012<br>"
94 " by Werner Lemberg <tt><wl@gnu.org></tt></p>"
96 "<p><b>TTFautohint</b> adds new auto-generated hints"
97 " to a TrueType font or TrueType collection.</p>"
100 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
101 " License (FTL)</a> or"
102 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
110 Main_GUI::browse_input()
112 // XXX remember last directory
113 QString file
= QFileDialog::getOpenFileName(
115 tr("Open Input File"),
119 input_line
->setText(QDir::toNativeSeparators(file
));
124 Main_GUI::browse_output()
126 // XXX remember last directory
127 QString file
= QFileDialog::getSaveFileName(
129 tr("Open Output File"),
134 output_line
->setText(QDir::toNativeSeparators(file
));
139 Main_GUI::check_min()
141 int min
= min_box
->value();
142 int max
= max_box
->value();
143 int limit
= limit_box
->value();
145 max_box
->setValue(min
);
147 limit_box
->setValue(min
);
152 Main_GUI::check_max()
154 int min
= min_box
->value();
155 int max
= max_box
->value();
156 int limit
= limit_box
->value();
158 min_box
->setValue(max
);
160 limit_box
->setValue(max
);
165 Main_GUI::check_limit()
167 int min
= min_box
->value();
168 int max
= max_box
->value();
169 int limit
= limit_box
->value();
171 max_box
->setValue(limit
);
173 min_box
->setValue(limit
);
178 Main_GUI::check_no_limit()
180 if (no_limit_box
->isChecked())
182 limit_label
->setEnabled(false);
183 limit_box
->setEnabled(false);
187 limit_label
->setEnabled(true);
188 limit_box
->setEnabled(true);
194 Main_GUI::check_run()
196 if (input_line
->text().isEmpty() || output_line
->text().isEmpty())
197 run_button
->setEnabled(false);
199 run_button
->setEnabled(true);
204 Main_GUI::absolute_input()
206 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
207 if (!input_name
.isEmpty()
208 && QDir::isRelativePath(input_name
))
210 QDir
cur_path(QDir::currentPath() + "/" + input_name
);
211 input_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
217 Main_GUI::absolute_output()
219 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
220 if (!output_name
.isEmpty()
221 && QDir::isRelativePath(output_name
))
223 QDir
cur_path(QDir::currentPath() + "/" + output_name
);
224 output_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
230 Main_GUI::check_filenames(const QString
& input_name
,
231 const QString
& output_name
)
233 if (!QFile::exists(input_name
))
235 QMessageBox::warning(
238 tr("The file %1 cannot be found.")
239 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
))),
245 if (input_name
== output_name
)
247 QMessageBox::warning(
250 tr("Input and output file names must be different."),
256 if (QFile::exists(output_name
))
258 int ret
= QMessageBox::warning(
261 tr("The file %1 already exists.\n"
263 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
))),
264 QMessageBox::Yes
| QMessageBox::No
,
266 if (ret
== QMessageBox::No
)
275 Main_GUI::open_files(const QString
& input_name
,
277 const QString
& output_name
,
280 const int buf_len
= 1024;
283 *in
= fopen(qPrintable(input_name
), "rb");
286 strerror_r(errno
, buf
, buf_len
);
287 QMessageBox::warning(
290 tr("The following error occurred while opening input file %1:\n")
291 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
)))
292 + QString::fromLocal8Bit(buf
),
298 *out
= fopen(qPrintable(output_name
), "wb");
301 strerror_r(errno
, buf
, buf_len
);
302 QMessageBox::warning(
305 tr("The following error occurred while opening output file %1:\n")
306 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
307 + QString::fromLocal8Bit(buf
),
319 struct GUI_Progress_Data
323 QProgressDialog
* dialog
;
328 gui_progress(long curr_idx
,
334 GUI_Progress_Data
* data
= (GUI_Progress_Data
*)user
;
336 if (num_sfnts
> 1 && curr_sfnt
!= data
->last_sfnt
)
338 data
->dialog
->setLabelText(QCoreApplication::translate(
340 "Auto-hinting subfont %1 of %2"
341 " with %3 glyphs...")
346 if (curr_sfnt
+ 1 == num_sfnts
)
348 data
->dialog
->setAutoReset(true);
349 data
->dialog
->setAutoClose(true);
353 data
->dialog
->setAutoReset(false);
354 data
->dialog
->setAutoClose(false);
357 data
->last_sfnt
= curr_sfnt
;
364 data
->dialog
->setLabelText(QCoreApplication::translate(
366 "Auto-hinting %1 glyphs...")
368 data
->dialog
->setMaximum(num_glyphs
- 1);
373 data
->dialog
->setValue(curr_idx
);
375 if (data
->dialog
->wasCanceled())
384 // return value 1 indicates a retry
387 Main_GUI::handle_error(TA_Error error
,
388 const unsigned char* error_string
,
393 if (error
== TA_Err_Canceled
)
395 else if (error
== TA_Err_Invalid_FreeType_Version
)
396 QMessageBox::critical(
399 tr("FreeType version 2.4.5 or higher is needed.\n"
400 "Are you perhaps using a wrong FreeType DLL?"),
403 else if (error
== TA_Err_Already_Processed
)
404 QMessageBox::warning(
407 tr("This font has already been processed by ttfautohint."),
410 else if (error
== TA_Err_Missing_Legal_Permission
)
412 int yesno
= QMessageBox::warning(
415 tr("Bit 1 in the %1 field of the %2 table is set:"
416 " This font must not be modified"
417 " without permission of the legal owner.\n"
418 "Do you have such a permission?")
419 .arg(QUOTE_STRING_LITERAL("fsType"))
420 .arg(QUOTE_STRING_LITERAL("OS/2")),
421 QMessageBox::Yes
| QMessageBox::No
,
423 if (yesno
== QMessageBox::Yes
)
425 ignore_permissions
= true;
429 else if (error
== TA_Err_Missing_Unicode_CMap
)
430 QMessageBox::warning(
433 tr("No Unicode character map."),
436 else if (error
== TA_Err_Missing_Glyph
)
437 QMessageBox::warning(
440 tr("No glyph for the key character"
441 " to derive standard stem width and height.\n"
442 "For the latin script, this key character is %1 (U+006F).\n"
444 "Set the %2 checkbox if you want to circumvent this test.")
445 .arg(QUOTE_STRING_LITERAL("o"))
446 .arg(QUOTE_STRING_LITERAL("symbol")),
450 QMessageBox::warning(
453 tr("Error code 0x%1 while autohinting font:\n")
454 .arg(error
, 2, 16, QLatin1Char('0'))
455 + QString::fromLocal8Bit((const char*)error_string
),
459 if (QFile::exists(output_name
) && remove(qPrintable(output_name
)))
461 const int buf_len
= 1024;
464 strerror_r(errno
, buf
, buf_len
);
465 QMessageBox::warning(
468 tr("The following error occurred while removing output file %1:\n")
469 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
470 + QString::fromLocal8Bit(buf
),
482 statusBar()->clearMessage();
484 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
485 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
486 if (!check_filenames(input_name
, output_name
))
489 // we need C file descriptors for communication with TTF_autohint
494 if (!open_files(input_name
, &input
, output_name
, &output
))
497 unsigned char version_data
[128];
498 unsigned char version_data_wide
[256];
500 QProgressDialog dialog
;
501 dialog
.setCancelButtonText(tr("Cancel"));
502 dialog
.setMinimumDuration(1000);
503 dialog
.setWindowModality(Qt::WindowModal
);
505 const unsigned char* error_string
;
506 TA_Info_Func info_func
= info
;
507 GUI_Progress_Data gui_progress_data
= {-1, true, &dialog
};
510 info_data
.data
= version_data
;
511 info_data
.data_wide
= version_data_wide
;
513 info_data
.hinting_range_min
= min_box
->value();
514 info_data
.hinting_range_max
= max_box
->value();
515 info_data
.hinting_limit
= no_limit_box
->isChecked() ? 0
516 : limit_box
->value();
518 info_data
.pre_hinting
= pre_box
->isChecked();
519 info_data
.increase_x_height
= increase_box
->isChecked();
520 info_data
.latin_fallback
= fallback_box
->currentIndex();
521 info_data
.symbol
= symbol_box
->isChecked();
523 if (info_box
->isChecked())
524 build_version_string(&info_data
);
529 TTF_autohint("in-file, out-file,"
530 "hinting-range-min, hinting-range-max,"
533 "progress-callback, progress-callback-data,"
534 "info-callback, info-callback-data,"
535 "ignore-permissions,"
536 "pre-hinting, increase-x-height,"
537 "fallback-script, symbol",
539 info_data
.hinting_range_min
, info_data
.hinting_range_max
,
540 info_data
.hinting_limit
,
542 gui_progress
, &gui_progress_data
,
543 info_func
, &info_data
,
545 info_data
.pre_hinting
, info_data
.increase_x_height
,
546 info_data
.latin_fallback
, info_data
.symbol
);
553 if (handle_error(error
, error_string
, output_name
))
557 statusBar()->showMessage(tr("Auto-hinting finished."));
562 Main_GUI::create_layout()
565 QCompleter
* completer
= new QCompleter(this);
566 QFileSystemModel
* model
= new QFileSystemModel(completer
);
567 model
->setRootPath(QDir::rootPath());
568 completer
->setModel(model
);
570 QLabel
* input_label
= new QLabel(tr("&Input File:"));
571 input_line
= new QLineEdit
;
572 input_button
= new QPushButton(tr("Browse..."));
573 input_label
->setBuddy(input_line
);
574 // enforce rich text to get nice word wrapping
575 input_label
->setToolTip(
576 tr("<b></b>The input file, either a TrueType font (TTF),"
577 " TrueType collection (TTC), or a TrueType-based OpenType font."));
578 input_line
->setCompleter(completer
);
580 QLabel
* output_label
= new QLabel(tr("&Output File:"));
581 output_line
= new QLineEdit
;
582 output_button
= new QPushButton(tr("Browse..."));
583 output_label
->setBuddy(output_line
);
584 output_label
->setToolTip(
585 tr("<b></b>The output file, which will be essentially identical"
586 " to the input font but contains new, generated hints."));
587 output_line
->setCompleter(completer
);
589 QGridLayout
* file_layout
= new QGridLayout
;
590 file_layout
->addWidget(input_label
, 0, 0);
591 file_layout
->addWidget(input_line
, 0, 1);
592 file_layout
->addWidget(input_button
, 0, 2);
593 file_layout
->addWidget(output_label
, 1, 0);
594 file_layout
->addWidget(output_line
, 1, 1);
595 file_layout
->addWidget(output_button
, 1, 2);
598 QLabel
* min_label
= new QLabel(tr("Mi&nimum:"));
599 min_box
= new QSpinBox
;
600 min_label
->setBuddy(min_box
);
601 min_box
->setKeyboardTracking(false);
602 min_box
->setRange(2, 10000);
603 min_box
->setValue(hinting_range_min
);
605 QLabel
* max_label
= new QLabel(tr("Ma&ximum:"));
606 max_box
= new QSpinBox
;
607 max_label
->setBuddy(max_box
);
608 max_box
->setKeyboardTracking(false);
609 max_box
->setRange(2, 10000);
610 max_box
->setValue(hinting_range_max
);
612 QGridLayout
* minmax_layout
= new QGridLayout
;
613 minmax_layout
->addWidget(min_label
, 0, 0);
614 minmax_layout
->addWidget(min_box
, 0, 1);
615 minmax_layout
->addWidget(max_label
, 1, 0);
616 minmax_layout
->addWidget(max_box
, 1, 1);
618 // hinting and fallback controls
619 QLabel
* hinting_label
= new QLabel(tr("Hint Set Range") + " ");
620 QLabel
* fallback_label
= new QLabel(tr("Fallback &Script:"));
621 hinting_label
->setToolTip(
622 tr("The PPEM range for which <b>TTFautohint</b> computes"
624 " A hint set for a given PPEM value hints this size optimally."
625 " The larger the range, the more hint sets are considered,"
626 " usually increasing the size of the bytecode.<br>"
627 "Note that changing this range doesn't influence"
628 " the <i>gasp</i> table:"
629 " Hinting is enabled for all sizes."));
630 fallback_box
= new QComboBox
;
631 fallback_label
->setBuddy(fallback_box
);
632 fallback_label
->setToolTip(
633 tr("This sets the fallback script module for glyphs"
634 " which <b>TTFautohint</b> can't map to a script automatically."));
635 fallback_box
->insertItem(0, tr("None"));
636 fallback_box
->insertItem(1, tr("Latin"));
637 fallback_box
->setCurrentIndex(latin_fallback
);
639 QHBoxLayout
* hint_fallback_layout
= new QHBoxLayout
;
640 hint_fallback_layout
->addWidget(hinting_label
);
641 hint_fallback_layout
->addLayout(minmax_layout
);
642 hint_fallback_layout
->addStretch(1);
643 hint_fallback_layout
->addWidget(fallback_label
);
644 hint_fallback_layout
->addWidget(fallback_box
);
645 hint_fallback_layout
->addStretch(2);
648 limit_label
= new QLabel(tr("Hinting &Limit:"));
649 limit_box
= new QSpinBox
;
650 limit_label
->setBuddy(limit_box
);
651 limit_label
->setToolTip(
652 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
653 " sizes larger than this PPEM value are not hinted"
654 " (regardless of the values in the <i>gasp</i> table)."));
655 limit_box
->setKeyboardTracking(false);
656 limit_box
->setRange(2, 10000);
657 limit_box
->setValue(hinting_limit
? hinting_limit
: hinting_range_max
);
659 no_limit_box
= new QCheckBox(tr("No Hinting Limi&t"), this);
660 no_limit_box
->setToolTip(
661 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
662 " to the bytecode."));
664 QHBoxLayout
* limit_layout
= new QHBoxLayout
;
665 limit_layout
->addWidget(limit_label
);
666 limit_layout
->addWidget(limit_box
);
667 limit_layout
->addStretch(1);
668 limit_layout
->addWidget(no_limit_box
);
669 limit_layout
->addStretch(2);
671 // handle command line option `--hinting-limit=0'
674 hinting_limit
= max_box
->value();
675 no_limit_box
->setChecked(true);
684 pre_box
= new QCheckBox(tr("Pr&e-hinting"), this);
686 tr("If switched on, the original bytecode of the input font"
687 " gets applied before <b>TTFautohint</b> starts processing"
688 " the outlines of the glyphs."));
690 pre_box
->setChecked(true);
691 increase_box
= new QCheckBox(tr("In&crease x-height"), this);
692 increase_box
->setToolTip(
693 tr("For PPEM values in the range 5 < PPEM < 15,"
694 " round up the font's x height much more often than normally"
695 " if switched on.<br>"
696 "Use this if holes in letters like <i>e</i> get filled,"
698 if (increase_x_height
)
699 increase_box
->setChecked(true);
700 symbol_box
= new QCheckBox(tr("S&ymbol Font"), this);
701 symbol_box
->setToolTip(
702 tr("If switched on, <b>ttfautohint</b> uses default values"
703 " for standard stem width and height"
704 " instead of deriving these values from the input font.<br>"
705 "Use this for fonts which don't contain glyphs"
706 " of a (supported) script."));
708 symbol_box
->setChecked(true);
710 QHBoxLayout
* flags_layout
= new QHBoxLayout
;
711 flags_layout
->addWidget(pre_box
);
712 flags_layout
->addStretch(1);
713 flags_layout
->addWidget(increase_box
);
714 flags_layout
->addStretch(1);
715 flags_layout
->addWidget(symbol_box
);
716 flags_layout
->addStretch(1);
719 info_box
= new QCheckBox(tr("add ttf&autohint info"), this);
720 info_box
->setToolTip(
721 tr("If switched on, information about <b>ttfautohint</b>"
722 " and its calling parameters are added to the version string(s)"
723 " (name ID 5) in the <i>name</i> table."));
725 info_box
->setChecked(true);
727 QHBoxLayout
* info_layout
= new QHBoxLayout
;
728 info_layout
->addWidget(info_box
);
731 run_button
= new QPushButton(tr("&Run"));
732 run_button
->setEnabled(false);
734 QHBoxLayout
* running_layout
= new QHBoxLayout
;
735 running_layout
->addStretch(1);
736 running_layout
->addWidget(run_button
);
737 running_layout
->addStretch(1);
740 QVBoxLayout
* gui_layout
= new QVBoxLayout
;
741 gui_layout
->addSpacing(10); // XXX urgh, pixels...
742 gui_layout
->addLayout(file_layout
);
743 gui_layout
->addSpacing(20); // XXX urgh, pixels...
744 gui_layout
->addLayout(hint_fallback_layout
);
745 gui_layout
->addSpacing(20); // XXX urgh, pixels...
746 gui_layout
->addLayout(limit_layout
);
747 gui_layout
->addSpacing(20); // XXX urgh, pixels...
748 gui_layout
->addLayout(flags_layout
);
749 gui_layout
->addSpacing(20); // XXX urgh, pixels...
750 gui_layout
->addLayout(info_layout
);
751 gui_layout
->addSpacing(20); // XXX urgh, pixels...
752 gui_layout
->addLayout(running_layout
);
753 gui_layout
->addSpacing(10); // XXX urgh, pixels...
755 // create dummy widget to register layout
756 QWidget
* main_widget
= new QWidget
;
757 main_widget
->setLayout(gui_layout
);
758 setCentralWidget(main_widget
);
759 setWindowTitle("TTFautohint");
764 Main_GUI::create_connections()
766 connect(input_button
, SIGNAL(clicked()), this,
767 SLOT(browse_input()));
768 connect(output_button
, SIGNAL(clicked()), this,
769 SLOT(browse_output()));
771 connect(input_line
, SIGNAL(textChanged(QString
)), this,
773 connect(output_line
, SIGNAL(textChanged(QString
)), this,
776 connect(input_line
, SIGNAL(editingFinished()), this,
777 SLOT(absolute_input()));
778 connect(output_line
, SIGNAL(editingFinished()), this,
779 SLOT(absolute_output()));
781 connect(min_box
, SIGNAL(valueChanged(int)), this,
783 connect(max_box
, SIGNAL(valueChanged(int)), this,
786 connect(limit_box
, SIGNAL(valueChanged(int)), this,
787 SLOT(check_limit()));
788 connect(no_limit_box
, SIGNAL(clicked()), this,
789 SLOT(check_no_limit()));
791 connect(run_button
, SIGNAL(clicked()), this,
797 Main_GUI::create_actions()
799 exit_act
= new QAction(tr("E&xit"), this);
800 exit_act
->setShortcuts(QKeySequence::Quit
);
801 connect(exit_act
, SIGNAL(triggered()), this, SLOT(close()));
803 about_act
= new QAction(tr("&About"), this);
804 connect(about_act
, SIGNAL(triggered()), this, SLOT(about()));
806 about_Qt_act
= new QAction(tr("About &Qt"), this);
807 connect(about_Qt_act
, SIGNAL(triggered()), qApp
, SLOT(aboutQt()));
812 Main_GUI::create_menus()
814 file_menu
= menuBar()->addMenu(tr("&File"));
815 file_menu
->addAction(exit_act
);
817 help_menu
= menuBar()->addMenu(tr("&Help"));
818 help_menu
->addAction(about_act
);
819 help_menu
->addAction(about_Qt_act
);
824 Main_GUI::create_status_bar()
826 statusBar()->showMessage("");
831 Main_GUI::read_settings()
834 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
835 // QSize size = settings.value("size", QSize(400, 400)).toSize();
842 Main_GUI::write_settings()
845 // settings.setValue("pos", pos());
846 // settings.setValue("size", size());
849 // end of maingui.cpp