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.
24 #include <ttfautohint.h>
27 // XXX Qt 4.8 bug: locale->quoteString("foo")
28 // inserts wrongly encoded quote characters
29 // into rich text QString
30 #if HAVE_QT_QUOTESTRING
31 # define QUOTE_STRING(x) locale->quoteString(x)
32 # define QUOTE_STRING_LITERAL(x) locale->quoteString(x)
34 # define QUOTE_STRING(x) "\"" + x + "\""
35 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
39 Main_GUI::Main_GUI(int range_min
,
46 : hinting_range_min(range_min
),
47 hinting_range_max(range_max
),
49 ignore_permissions(ignore
),
51 increase_x_height(increase
),
52 latin_fallback(fallback
)
62 setUnifiedTitleAndToolBarOnMac(true);
64 // XXX register translations somewhere and loop over them
65 if (QLocale::system().name() == "en_US")
68 locale
= new QLocale(QLocale::C
);
75 Main_GUI::closeEvent(QCloseEvent
* event
)
85 QMessageBox::about(this,
86 tr("About TTFautohint"),
87 tr("<p>This is <b>TTFautohint</b> version %1<br>"
88 " Copyright %2 2011-2012<br>"
89 " by Werner Lemberg <tt><wl@gnu.org></tt></p>"
91 "<p><b>TTFautohint</b> adds new auto-generated hints"
92 " to a TrueType font or TrueType collection.</p>"
95 " <a href='http://www.freetype.org/FTL.TXT'>FreeType"
96 " License (FTL)</a> or"
97 " <a href='http://www.freetype.org/GPL.TXT'>GNU"
105 Main_GUI::browse_input()
107 // XXX remember last directory
108 QString file
= QFileDialog::getOpenFileName(
110 tr("Open Input File"),
114 input_line
->setText(QDir::toNativeSeparators(file
));
119 Main_GUI::browse_output()
121 // XXX remember last directory
122 QString file
= QFileDialog::getSaveFileName(
124 tr("Open Output File"),
129 output_line
->setText(QDir::toNativeSeparators(file
));
134 Main_GUI::check_min()
136 int min
= min_box
->value();
137 int max
= max_box
->value();
138 int limit
= limit_box
->value();
140 max_box
->setValue(min
);
142 limit_box
->setValue(min
);
147 Main_GUI::check_max()
149 int min
= min_box
->value();
150 int max
= max_box
->value();
151 int limit
= limit_box
->value();
153 min_box
->setValue(max
);
155 limit_box
->setValue(max
);
160 Main_GUI::check_limit()
162 int min
= min_box
->value();
163 int max
= max_box
->value();
164 int limit
= limit_box
->value();
166 max_box
->setValue(limit
);
168 min_box
->setValue(limit
);
173 Main_GUI::check_no_limit()
175 if (no_limit_box
->isChecked())
177 limit_label
->setEnabled(false);
178 limit_box
->setEnabled(false);
182 limit_label
->setEnabled(true);
183 limit_box
->setEnabled(true);
189 Main_GUI::check_run()
191 if (input_line
->text().isEmpty() || output_line
->text().isEmpty())
192 run_button
->setEnabled(false);
194 run_button
->setEnabled(true);
199 Main_GUI::absolute_input()
201 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
202 if (!input_name
.isEmpty()
203 && QDir::isRelativePath(input_name
))
205 QDir
cur_path(QDir::currentPath() + "/" + input_name
);
206 input_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
212 Main_GUI::absolute_output()
214 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
215 if (!output_name
.isEmpty()
216 && QDir::isRelativePath(output_name
))
218 QDir
cur_path(QDir::currentPath() + "/" + output_name
);
219 output_line
->setText(QDir::toNativeSeparators(cur_path
.absolutePath()));
225 Main_GUI::check_filenames(const QString
& input_name
,
226 const QString
& output_name
)
228 if (!QFile::exists(input_name
))
230 QMessageBox::warning(
233 tr("The file %1 cannot be found.")
234 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
))),
240 if (input_name
== output_name
)
242 QMessageBox::warning(
245 tr("Input and output file names must be different."),
251 if (QFile::exists(output_name
))
253 int ret
= QMessageBox::warning(
256 tr("The file %1 already exists.\n"
258 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
))),
259 QMessageBox::Yes
| QMessageBox::No
,
261 if (ret
== QMessageBox::No
)
270 Main_GUI::open_files(const QString
& input_name
,
272 const QString
& output_name
,
275 const int buf_len
= 1024;
278 *in
= fopen(qPrintable(input_name
), "rb");
281 strerror_r(errno
, buf
, buf_len
);
282 QMessageBox::warning(
285 tr("The following error occurred while opening input file %1:\n")
286 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name
)))
287 + QString::fromLocal8Bit(buf
),
293 *out
= fopen(qPrintable(output_name
), "wb");
296 strerror_r(errno
, buf
, buf_len
);
297 QMessageBox::warning(
300 tr("The following error occurred while opening output file %1:\n")
301 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
302 + QString::fromLocal8Bit(buf
),
314 struct GUI_Progress_Data
318 QProgressDialog
* dialog
;
323 gui_progress(long curr_idx
,
329 GUI_Progress_Data
* data
= (GUI_Progress_Data
*)user
;
331 if (num_sfnts
> 1 && curr_sfnt
!= data
->last_sfnt
)
333 data
->dialog
->setLabelText(QCoreApplication::translate(
335 "Auto-hinting subfont %1 of %2"
336 " with %3 glyphs...")
341 if (curr_sfnt
+ 1 == num_sfnts
)
343 data
->dialog
->setAutoReset(true);
344 data
->dialog
->setAutoClose(true);
348 data
->dialog
->setAutoReset(false);
349 data
->dialog
->setAutoClose(false);
352 data
->last_sfnt
= curr_sfnt
;
359 data
->dialog
->setLabelText(QCoreApplication::translate(
361 "Auto-hinting %1 glyphs...")
363 data
->dialog
->setMaximum(num_glyphs
- 1);
368 data
->dialog
->setValue(curr_idx
);
370 if (data
->dialog
->wasCanceled())
379 // return value 1 indicates a retry
382 Main_GUI::handle_error(TA_Error error
,
383 const unsigned char* error_string
,
388 if (error
== TA_Err_Canceled
)
390 else if (error
== TA_Err_Invalid_FreeType_Version
)
391 QMessageBox::critical(
394 tr("FreeType version 2.4.5 or higher is needed.\n"
395 "Are you perhaps using a wrong FreeType DLL?"),
398 else if (error
== TA_Err_Already_Processed
)
399 QMessageBox::warning(
402 tr("This font has already been processed by ttfautohint."),
405 else if (error
== TA_Err_Missing_Legal_Permission
)
407 int yesno
= QMessageBox::warning(
410 tr("Bit 1 in the %1 field of the %2 table is set:"
411 " This font must not be modified"
412 " without permission of the legal owner.\n"
413 "Do you have such a permission?")
414 .arg(QUOTE_STRING_LITERAL("fsType"))
415 .arg(QUOTE_STRING_LITERAL("OS/2")),
416 QMessageBox::Yes
| QMessageBox::No
,
418 if (yesno
== QMessageBox::Yes
)
420 ignore_permissions
= true;
424 else if (error
== TA_Err_Missing_Unicode_CMap
)
425 QMessageBox::warning(
428 tr("No Unicode character map."),
431 else if (error
== TA_Err_Missing_Glyph
)
432 QMessageBox::warning(
435 tr("No glyph for the key character"
436 " to derive standard width and height.\n"
437 "For the latin script, this key character is %1 (U+006F).")
438 .arg(QUOTE_STRING_LITERAL("o")),
442 QMessageBox::warning(
445 tr("Error code 0x%1 while autohinting font:\n")
446 .arg(error
, 2, 16, QLatin1Char('0'))
447 + QString::fromLocal8Bit((const char*)error_string
),
451 if (QFile::exists(output_name
) && remove(qPrintable(output_name
)))
453 const int buf_len
= 1024;
456 strerror_r(errno
, buf
, buf_len
);
457 QMessageBox::warning(
460 tr("The following error occurred while removing output file %1:\n")
461 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name
)))
462 + QString::fromLocal8Bit(buf
),
474 statusBar()->clearMessage();
476 QString input_name
= QDir::fromNativeSeparators(input_line
->text());
477 QString output_name
= QDir::fromNativeSeparators(output_line
->text());
478 if (!check_filenames(input_name
, output_name
))
481 // we need C file descriptors for communication with TTF_autohint
486 if (!open_files(input_name
, &input
, output_name
, &output
))
489 QProgressDialog dialog
;
490 dialog
.setCancelButtonText(tr("Cancel"));
491 dialog
.setMinimumDuration(1000);
492 dialog
.setWindowModality(Qt::WindowModal
);
494 GUI_Progress_Data gui_progress_data
= {-1, true, &dialog
};
495 const unsigned char* error_string
;
498 TTF_autohint("in-file, out-file,"
499 "hinting-range-min, hinting-range-max,"
502 "progress-callback, progress-callback-data,"
503 "ignore-permissions,"
504 "pre-hinting, increase-x-height,"
507 min_box
->value(), max_box
->value(),
508 no_limit_box
->isChecked() ? 0 : limit_box
->value(),
510 gui_progress
, &gui_progress_data
,
512 pre_box
->isChecked(), increase_box
->isChecked(),
513 fallback_box
->currentIndex());
520 if (handle_error(error
, error_string
, output_name
))
524 statusBar()->showMessage(tr("Auto-hinting finished."));
529 Main_GUI::create_layout()
532 QCompleter
* completer
= new QCompleter(this);
533 QFileSystemModel
* model
= new QFileSystemModel(completer
);
534 model
->setRootPath(QDir::rootPath());
535 completer
->setModel(model
);
537 QLabel
* input_label
= new QLabel(tr("&Input File:"));
538 input_line
= new QLineEdit
;
539 input_button
= new QPushButton(tr("Browse..."));
540 input_label
->setBuddy(input_line
);
541 // enforce rich text to get nice word wrapping
542 input_label
->setToolTip(
543 tr("<b></b>The input file, either a TrueType font (TTF),"
544 " TrueType collection (TTC), or a TrueType-based OpenType font."));
545 input_line
->setCompleter(completer
);
547 QLabel
* output_label
= new QLabel(tr("&Output File:"));
548 output_line
= new QLineEdit
;
549 output_button
= new QPushButton(tr("Browse..."));
550 output_label
->setBuddy(output_line
);
551 output_label
->setToolTip(
552 tr("<b></b>The output file, which will be essentially identical"
553 " to the input font but contains new, generated hints."));
554 output_line
->setCompleter(completer
);
556 QGridLayout
* file_layout
= new QGridLayout
;
557 file_layout
->addWidget(input_label
, 0, 0);
558 file_layout
->addWidget(input_line
, 0, 1);
559 file_layout
->addWidget(input_button
, 0, 2);
560 file_layout
->addWidget(output_label
, 1, 0);
561 file_layout
->addWidget(output_line
, 1, 1);
562 file_layout
->addWidget(output_button
, 1, 2);
565 QLabel
* min_label
= new QLabel(tr("Mi&nimum:"));
566 min_box
= new QSpinBox
;
567 min_label
->setBuddy(min_box
);
568 min_box
->setKeyboardTracking(false);
569 min_box
->setRange(2, 10000);
570 min_box
->setValue(hinting_range_min
);
572 QLabel
* max_label
= new QLabel(tr("Ma&ximum:"));
573 max_box
= new QSpinBox
;
574 max_label
->setBuddy(max_box
);
575 max_box
->setKeyboardTracking(false);
576 max_box
->setRange(2, 10000);
577 max_box
->setValue(hinting_range_max
);
579 QGridLayout
* minmax_layout
= new QGridLayout
;
580 minmax_layout
->addWidget(min_label
, 0, 0);
581 minmax_layout
->addWidget(min_box
, 0, 1);
582 minmax_layout
->addWidget(max_label
, 1, 0);
583 minmax_layout
->addWidget(max_box
, 1, 1);
585 // hinting and fallback controls
586 QLabel
* hinting_label
= new QLabel(tr("Hint Set Range") + " ");
587 QLabel
* fallback_label
= new QLabel(tr("F&allback Script:"));
588 hinting_label
->setToolTip(
589 tr("The PPEM range for which <b>TTFautohint</b> computes"
591 " A hint set for a given PPEM value hints this size optimally."
592 " The larger the range, the more hint sets are considered,"
593 " usually increasing the size of the bytecode.\n"
594 "Note that changing this range doesn't influence"
595 " the <i>gasp</i> table:"
596 " Hinting is enabled for all sizes."));
597 fallback_box
= new QComboBox
;
598 fallback_label
->setBuddy(fallback_box
);
599 fallback_label
->setToolTip(
600 tr("This sets the fallback script module for glyphs"
601 " which <b>TTFautohint</b> can't map to a script automatically."));
602 fallback_box
->insertItem(0, tr("None"));
603 fallback_box
->insertItem(1, tr("Latin"));
604 fallback_box
->setCurrentIndex(latin_fallback
);
606 QHBoxLayout
* hint_fallback_layout
= new QHBoxLayout
;
607 hint_fallback_layout
->addWidget(hinting_label
);
608 hint_fallback_layout
->addLayout(minmax_layout
);
609 hint_fallback_layout
->addStretch(1);
610 hint_fallback_layout
->addWidget(fallback_label
);
611 hint_fallback_layout
->addWidget(fallback_box
);
612 hint_fallback_layout
->addStretch(2);
615 limit_label
= new QLabel(tr("Hinting &Limit:"));
616 limit_box
= new QSpinBox
;
617 limit_label
->setBuddy(limit_box
);
618 limit_label
->setToolTip(
619 tr("Make <b>TTFautohint</b> add bytecode to the output font so that"
620 " sizes larger than this PPEM value are not hinted"
621 " (regardless of the values in the <i>gasp</i> table)."));
622 limit_box
->setKeyboardTracking(false);
623 limit_box
->setRange(2, 10000);
624 limit_box
->setValue(hinting_limit
? hinting_limit
: hinting_range_max
);
626 no_limit_box
= new QCheckBox(tr("No Hinting Limi&t"), this);
627 no_limit_box
->setToolTip(
628 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
629 " to the bytecode."));
631 QHBoxLayout
* limit_layout
= new QHBoxLayout
;
632 limit_layout
->addWidget(limit_label
);
633 limit_layout
->addWidget(limit_box
);
634 limit_layout
->addStretch(1);
635 limit_layout
->addWidget(no_limit_box
);
636 limit_layout
->addStretch(2);
638 // handle command line option `--hinting-limit=0'
641 hinting_limit
= max_box
->value();
642 no_limit_box
->setChecked(true);
651 pre_box
= new QCheckBox(tr("Pr&e-hinting"), this);
653 tr("If switched on, the original bytecode of the input font"
654 " gets applied before <b>TTFautohint</b> starts processing"
655 " the outlines of the glyphs."));
657 pre_box
->setChecked(true);
658 increase_box
= new QCheckBox(tr("In&crease x-height"), this);
659 increase_box
->setToolTip(
660 tr("For PPEM values in the range 5 < PPEM < 15,"
661 " round up the font's x height much more often than normally"
663 "Use this if holes in letters like <i>e</i> get filled,"
665 if (increase_x_height
)
666 increase_box
->setChecked(true);
668 QHBoxLayout
* flags_layout
= new QHBoxLayout
;
669 flags_layout
->addWidget(pre_box
);
670 flags_layout
->addStretch(1);
671 flags_layout
->addWidget(increase_box
);
672 flags_layout
->addStretch(2);
675 run_button
= new QPushButton(tr("&Run"));
676 run_button
->setEnabled(false);
678 QHBoxLayout
* running_layout
= new QHBoxLayout
;
679 running_layout
->addStretch(1);
680 running_layout
->addWidget(run_button
);
681 running_layout
->addStretch(1);
684 QVBoxLayout
* gui_layout
= new QVBoxLayout
;
685 gui_layout
->addSpacing(10); // XXX urgh, pixels...
686 gui_layout
->addLayout(file_layout
);
687 gui_layout
->addSpacing(20); // XXX urgh, pixels...
688 gui_layout
->addLayout(hint_fallback_layout
);
689 gui_layout
->addSpacing(20); // XXX urgh, pixels...
690 gui_layout
->addLayout(limit_layout
);
691 gui_layout
->addSpacing(20); // XXX urgh, pixels...
692 gui_layout
->addLayout(flags_layout
);
693 gui_layout
->addSpacing(20); // XXX urgh, pixels...
694 gui_layout
->addLayout(running_layout
);
695 gui_layout
->addSpacing(10); // XXX urgh, pixels...
697 // create dummy widget to register layout
698 QWidget
* main_widget
= new QWidget
;
699 main_widget
->setLayout(gui_layout
);
700 setCentralWidget(main_widget
);
701 setWindowTitle("TTFautohint");
706 Main_GUI::create_connections()
708 connect(input_button
, SIGNAL(clicked()), this,
709 SLOT(browse_input()));
710 connect(output_button
, SIGNAL(clicked()), this,
711 SLOT(browse_output()));
713 connect(input_line
, SIGNAL(textChanged(QString
)), this,
715 connect(output_line
, SIGNAL(textChanged(QString
)), this,
718 connect(input_line
, SIGNAL(editingFinished()), this,
719 SLOT(absolute_input()));
720 connect(output_line
, SIGNAL(editingFinished()), this,
721 SLOT(absolute_output()));
723 connect(min_box
, SIGNAL(valueChanged(int)), this,
725 connect(max_box
, SIGNAL(valueChanged(int)), this,
728 connect(limit_box
, SIGNAL(valueChanged(int)), this,
729 SLOT(check_limit()));
730 connect(no_limit_box
, SIGNAL(clicked()), this,
731 SLOT(check_no_limit()));
733 connect(run_button
, SIGNAL(clicked()), this,
739 Main_GUI::create_actions()
741 exit_act
= new QAction(tr("E&xit"), this);
742 exit_act
->setShortcuts(QKeySequence::Quit
);
743 connect(exit_act
, SIGNAL(triggered()), this, SLOT(close()));
745 about_act
= new QAction(tr("&About"), this);
746 connect(about_act
, SIGNAL(triggered()), this, SLOT(about()));
748 about_Qt_act
= new QAction(tr("About &Qt"), this);
749 connect(about_Qt_act
, SIGNAL(triggered()), qApp
, SLOT(aboutQt()));
754 Main_GUI::create_menus()
756 file_menu
= menuBar()->addMenu(tr("&File"));
757 file_menu
->addAction(exit_act
);
759 help_menu
= menuBar()->addMenu(tr("&Help"));
760 help_menu
->addAction(about_act
);
761 help_menu
->addAction(about_Qt_act
);
766 Main_GUI::create_status_bar()
768 statusBar()->showMessage("");
773 Main_GUI::read_settings()
776 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
777 // QSize size = settings.value("size", QSize(400, 400)).toSize();
784 Main_GUI::write_settings()
787 // settings.setValue("pos", pos());
788 // settings.setValue("size", size());
791 // end of maingui.cpp