[gui] Pass missing command line flags.
[ttfautohint.git] / frontend / maingui.cpp
blob9d57f2ad0b63ef66f239ab848ab95378a2d59ed7
1 // maingui.cpp
3 // Copyright (C) 2012 by Werner Lemberg.
4 //
5 // This file is part of the ttfautohint library, and may only be used,
6 // modified, and distributed under the terms given in `COPYING'. By
7 // continuing to use, modify, or distribute this file you indicate that you
8 // have read `COPYING' and understand and accept it fully.
9 //
10 // The file `COPYING' mentioned in the previous paragraph is distributed
11 // with the ttfautohint library.
14 #include <config.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <errno.h>
20 #include <QtGui>
22 #include "maingui.h"
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)
33 #else
34 # define QUOTE_STRING(x) "\"" + x + "\""
35 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
36 #endif
39 Main_GUI::Main_GUI(int range_min,
40 int range_max,
41 int limit,
42 bool ignore,
43 bool pre,
44 bool increase,
45 int fallback)
46 : hinting_range_min(range_min),
47 hinting_range_max(range_max),
48 hinting_limit(limit),
49 ignore_permissions(ignore),
50 pre_hinting(pre),
51 increase_x_height(increase),
52 latin_fallback(fallback)
54 create_layout();
55 create_connections();
56 create_actions();
57 create_menus();
58 create_status_bar();
60 read_settings();
62 setUnifiedTitleAndToolBarOnMac(true);
64 // XXX register translations somewhere and loop over them
65 if (QLocale::system().name() == "en_US")
66 locale = new QLocale;
67 else
68 locale = new QLocale(QLocale::C);
72 // overloading
74 void
75 Main_GUI::closeEvent(QCloseEvent* event)
77 write_settings();
78 event->accept();
82 void
83 Main_GUI::about()
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>&lt;wl@gnu.org&gt;</tt></p>"
91 "<p><b>TTFautohint</b> adds new auto-generated hints"
92 " to a TrueType font or TrueType collection.</p>"
94 "<p>License:"
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"
98 " GPLv2</a></p>")
99 .arg(VERSION)
100 .arg(QChar(0xA9)));
104 void
105 Main_GUI::browse_input()
107 // XXX remember last directory
108 QString file = QFileDialog::getOpenFileName(
109 this,
110 tr("Open Input File"),
111 QDir::homePath(),
112 "");
113 if (!file.isEmpty())
114 input_line->setText(QDir::toNativeSeparators(file));
118 void
119 Main_GUI::browse_output()
121 // XXX remember last directory
122 QString file = QFileDialog::getSaveFileName(
123 this,
124 tr("Open Output File"),
125 QDir::homePath(),
126 "");
128 if (!file.isEmpty())
129 output_line->setText(QDir::toNativeSeparators(file));
133 void
134 Main_GUI::check_min()
136 int min = min_box->value();
137 int max = max_box->value();
138 int limit = limit_box->value();
139 if (min > max)
140 max_box->setValue(min);
141 if (min > limit)
142 limit_box->setValue(min);
146 void
147 Main_GUI::check_max()
149 int min = min_box->value();
150 int max = max_box->value();
151 int limit = limit_box->value();
152 if (max < min)
153 min_box->setValue(max);
154 if (max > limit)
155 limit_box->setValue(max);
159 void
160 Main_GUI::check_limit()
162 int min = min_box->value();
163 int max = max_box->value();
164 int limit = limit_box->value();
165 if (limit < max)
166 max_box->setValue(limit);
167 if (limit < min)
168 min_box->setValue(limit);
172 void
173 Main_GUI::check_no_limit()
175 if (no_limit_box->isChecked())
177 limit_label->setEnabled(false);
178 limit_box->setEnabled(false);
180 else
182 limit_label->setEnabled(true);
183 limit_box->setEnabled(true);
188 void
189 Main_GUI::check_run()
191 if (input_line->text().isEmpty() || output_line->text().isEmpty())
192 run_button->setEnabled(false);
193 else
194 run_button->setEnabled(true);
198 void
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()));
211 void
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(
231 this,
232 "TTFautohint",
233 tr("The file %1 cannot be found.")
234 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
235 QMessageBox::Ok,
236 QMessageBox::Ok);
237 return 0;
240 if (input_name == output_name)
242 QMessageBox::warning(
243 this,
244 "TTFautohint",
245 tr("Input and output file names must be different."),
246 QMessageBox::Ok,
247 QMessageBox::Ok);
248 return 0;
251 if (QFile::exists(output_name))
253 int ret = QMessageBox::warning(
254 this,
255 "TTFautohint",
256 tr("The file %1 already exists.\n"
257 "Overwrite?")
258 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
259 QMessageBox::Yes | QMessageBox::No,
260 QMessageBox::No);
261 if (ret == QMessageBox::No)
262 return 0;
265 return 1;
270 Main_GUI::open_files(const QString& input_name,
271 FILE** in,
272 const QString& output_name,
273 FILE** out)
275 const int buf_len = 1024;
276 char buf[buf_len];
278 *in = fopen(qPrintable(input_name), "rb");
279 if (!*in)
281 strerror_r(errno, buf, buf_len);
282 QMessageBox::warning(
283 this,
284 "TTFautohint",
285 tr("The following error occurred while opening input file %1:\n")
286 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
287 + QString::fromLocal8Bit(buf),
288 QMessageBox::Ok,
289 QMessageBox::Ok);
290 return 0;
293 *out = fopen(qPrintable(output_name), "wb");
294 if (!*out)
296 strerror_r(errno, buf, buf_len);
297 QMessageBox::warning(
298 this,
299 "TTFautohint",
300 tr("The following error occurred while opening output file %1:\n")
301 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
302 + QString::fromLocal8Bit(buf),
303 QMessageBox::Ok,
304 QMessageBox::Ok);
305 return 0;
308 return 1;
312 extern "C" {
314 struct GUI_Progress_Data
316 long last_sfnt;
317 bool begin;
318 QProgressDialog* dialog;
323 gui_progress(long curr_idx,
324 long num_glyphs,
325 long curr_sfnt,
326 long num_sfnts,
327 void* user)
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(
334 "GuiProgress",
335 "Auto-hinting subfont %1 of %2"
336 " with %3 glyphs...")
337 .arg(curr_sfnt + 1)
338 .arg(num_sfnts)
339 .arg(num_glyphs));
341 if (curr_sfnt + 1 == num_sfnts)
343 data->dialog->setAutoReset(true);
344 data->dialog->setAutoClose(true);
346 else
348 data->dialog->setAutoReset(false);
349 data->dialog->setAutoClose(false);
352 data->last_sfnt = curr_sfnt;
353 data->begin = true;
356 if (data->begin)
358 if (num_sfnts == 1)
359 data->dialog->setLabelText(QCoreApplication::translate(
360 "GuiProgress",
361 "Auto-hinting %1 glyphs...")
362 .arg(num_glyphs));
363 data->dialog->setMaximum(num_glyphs - 1);
365 data->begin = false;
368 data->dialog->setValue(curr_idx);
370 if (data->dialog->wasCanceled())
371 return 1;
373 return 0;
376 } // extern "C"
379 // return value 1 indicates a retry
382 Main_GUI::handle_error(TA_Error error,
383 const unsigned char* error_string,
384 QString output_name)
386 int ret = 0;
388 if (error == TA_Err_Canceled)
390 else if (error == TA_Err_Invalid_FreeType_Version)
391 QMessageBox::critical(
392 this,
393 "TTFautohint",
394 tr("FreeType version 2.4.5 or higher is needed.\n"
395 "Are you perhaps using a wrong FreeType DLL?"),
396 QMessageBox::Ok,
397 QMessageBox::Ok);
398 else if (error == TA_Err_Already_Processed)
399 QMessageBox::warning(
400 this,
401 "TTFautohint",
402 tr("This font has already been processed by ttfautohint."),
403 QMessageBox::Ok,
404 QMessageBox::Ok);
405 else if (error == TA_Err_Missing_Legal_Permission)
407 int yesno = QMessageBox::warning(
408 this,
409 "TTFautohint",
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,
417 QMessageBox::No);
418 if (yesno == QMessageBox::Yes)
420 ignore_permissions = true;
421 ret = 1;
424 else if (error == TA_Err_Missing_Unicode_CMap)
425 QMessageBox::warning(
426 this,
427 "TTFautohint",
428 tr("No Unicode character map."),
429 QMessageBox::Ok,
430 QMessageBox::Ok);
431 else if (error == TA_Err_Missing_Glyph)
432 QMessageBox::warning(
433 this,
434 "TTFautohint",
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")),
439 QMessageBox::Ok,
440 QMessageBox::Ok);
441 else
442 QMessageBox::warning(
443 this,
444 "TTFautohint",
445 tr("Error code 0x%1 while autohinting font:\n")
446 .arg(error, 2, 16, QLatin1Char('0'))
447 + QString::fromLocal8Bit((const char*)error_string),
448 QMessageBox::Ok,
449 QMessageBox::Ok);
451 if (QFile::exists(output_name) && remove(qPrintable(output_name)))
453 const int buf_len = 1024;
454 char buf[buf_len];
456 strerror_r(errno, buf, buf_len);
457 QMessageBox::warning(
458 this,
459 "TTFautohint",
460 tr("The following error occurred while removing output file %1:\n")
461 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
462 + QString::fromLocal8Bit(buf),
463 QMessageBox::Ok,
464 QMessageBox::Ok);
467 return ret;
471 void
472 Main_GUI::run()
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))
479 return;
481 // we need C file descriptors for communication with TTF_autohint
482 FILE* input;
483 FILE* output;
485 again:
486 if (!open_files(input_name, &input, output_name, &output))
487 return;
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;
497 TA_Error error =
498 TTF_autohint("in-file, out-file,"
499 "hinting-range-min, hinting-range-max,"
500 "hinting-limit,"
501 "error-string,"
502 "progress-callback, progress-callback-data,"
503 "ignore-permissions,"
504 "pre-hinting, increase-x-height,"
505 "fallback-script",
506 input, output,
507 min_box->value(), max_box->value(),
508 no_limit_box->isChecked() ? 0 : limit_box->value(),
509 &error_string,
510 gui_progress, &gui_progress_data,
511 ignore_permissions,
512 pre_box->isChecked(), increase_box->isChecked(),
513 fallback_box->currentIndex());
515 fclose(input);
516 fclose(output);
518 if (error)
520 if (handle_error(error, error_string, output_name))
521 goto again;
523 else
524 statusBar()->showMessage(tr("Auto-hinting finished."));
528 void
529 Main_GUI::create_layout()
531 // file stuff
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);
564 // minmax controls
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"
590 " <i>hint sets</i>."
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);
614 // hinting limit
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'
639 if (!hinting_limit)
641 hinting_limit = max_box->value();
642 no_limit_box->setChecked(true);
645 check_min();
646 check_max();
647 check_limit();
648 check_no_limit();
650 // flags
651 pre_box = new QCheckBox(tr("Pr&e-hinting"), this);
652 pre_box->setToolTip(
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."));
656 if (pre_hinting)
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&nbsp;&lt; PPEM &lt;&nbsp;15,"
661 " round up the font's x&nbsp;height much more often than normally"
662 " if switched on.\n"
663 "Use this if holes in letters like <i>e</i> get filled,"
664 " for example."));
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);
674 // running
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);
683 // the whole gui
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");
705 void
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,
714 SLOT(check_run()));
715 connect(output_line, SIGNAL(textChanged(QString)), this,
716 SLOT(check_run()));
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,
724 SLOT(check_min()));
725 connect(max_box, SIGNAL(valueChanged(int)), this,
726 SLOT(check_max()));
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,
734 SLOT(run()));
738 void
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()));
753 void
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);
765 void
766 Main_GUI::create_status_bar()
768 statusBar()->showMessage("");
772 void
773 Main_GUI::read_settings()
775 QSettings settings;
776 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
777 // QSize size = settings.value("size", QSize(400, 400)).toSize();
778 // resize(size);
779 // move(pos);
783 void
784 Main_GUI::write_settings()
786 QSettings settings;
787 // settings.setValue("pos", pos());
788 // settings.setValue("size", size());
791 // end of maingui.cpp