Open delta exceptions file in text mode.
[ttfautohint.git] / frontend / maingui.cpp
bloba05d4ecf589237f3477ddc435df8c8fbfa9e26fa
1 // maingui.cpp
3 // Copyright (C) 2012-2014 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 "info.h"
23 #include "maingui.h"
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)
34 #else
35 # define QUOTE_STRING(x) "\"" + x + "\""
36 # define QUOTE_STRING_LITERAL(x) "\"" x "\""
37 #endif
40 // Shorthand for `tr' using a local `TRDOMAIN'.
41 #define Tr(text) QCoreApplication::translate(TRDOMAIN, text)
44 // the available script tags and its descriptions are directly extracted
45 // from `ttfautohint-scripts.h'
46 typedef struct Script_Names_
48 const char* tag;
49 const char* description;
50 } Script_Names;
52 #undef SCRIPT
53 #define SCRIPT(s, S, d, h, sc1, sc2, sc3) \
54 {#s, d},
56 const Script_Names script_names[] =
58 #include <ttfautohint-scripts.h>
59 {NULL, NULL}
63 Main_GUI::Main_GUI(bool horizontal_layout,
64 int range_min,
65 int range_max,
66 int limit,
67 bool gray,
68 bool gdi,
69 bool dw,
70 int increase,
71 const char* exceptions,
72 int stem_width,
73 bool ignore,
74 bool wincomp,
75 bool adjust,
76 bool composites,
77 bool no,
78 const char* dflt,
79 const char* fallback,
80 bool symb,
81 bool dh)
82 : hinting_range_min(range_min),
83 hinting_range_max(range_max),
84 hinting_limit(limit),
85 gray_strong_stem_width(gray),
86 gdi_cleartype_strong_stem_width(gdi),
87 dw_cleartype_strong_stem_width(dw),
88 increase_x_height(increase),
89 x_height_snapping_exceptions_string(exceptions),
90 fallback_stem_width(stem_width),
91 ignore_restrictions(ignore),
92 windows_compatibility(wincomp),
93 adjust_subglyphs(adjust),
94 hint_composites(composites),
95 no_info(no),
96 symbol(symb),
97 dehint(dh)
99 int i;
101 // map default script tag to an index,
102 // replacing an invalid one with the default value
103 int latn_script_idx = 0;
104 for (i = 0; script_names[i].tag; i++)
106 if (!strcmp("latn", script_names[i].tag))
107 latn_script_idx = i;
108 if (!strcmp(dflt, script_names[i].tag))
109 break;
111 default_script_idx = script_names[i].tag ? i : latn_script_idx;
113 // map fallback script tag to an index,
114 // replacing an invalid one with the default value
115 int none_script_idx = 0;
116 for (i = 0; script_names[i].tag; i++)
118 if (!strcmp("none", script_names[i].tag))
119 none_script_idx = i;
120 if (!strcmp(fallback, script_names[i].tag))
121 break;
123 fallback_script_idx = script_names[i].tag ? i : none_script_idx;
125 x_height_snapping_exceptions = NULL;
127 // if file watching is active, we regularly poll the file status
128 timer = new QTimer(this);
129 timer->setInterval(1000);
130 fileinfo_input_file.setCaching(false);
131 fileinfo_deltas_file.setCaching(false);
133 create_layout(horizontal_layout);
134 create_connections();
135 create_actions();
136 create_menus();
137 create_status_bar();
139 set_defaults();
140 read_settings();
142 setUnifiedTitleAndToolBarOnMac(true);
144 // XXX register translations somewhere and loop over them
145 if (QLocale::system().name() == "en_US")
146 locale = new QLocale;
147 else
148 locale = new QLocale(QLocale::C);
152 Main_GUI::~Main_GUI()
154 number_set_free(x_height_snapping_exceptions);
158 // overloading
160 void
161 Main_GUI::closeEvent(QCloseEvent* event)
163 write_settings();
164 event->accept();
168 void
169 Main_GUI::about()
171 QMessageBox::about(this,
172 tr("About TTFautohint"),
173 tr("<p>This is <b>TTFautohint</b> version %1<br>"
174 " Copyright %2 2011-2014<br>"
175 " by Werner Lemberg <tt>&lt;wl@gnu.org&gt;</tt></p>"
177 "<p><b>TTFautohint</b> adds new auto-generated hints"
178 " to a TrueType font or TrueType collection.</p>"
180 "<p>License:"
181 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT'>FreeType"
182 " License (FTL)</a> or"
183 " <a href='http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT'>GNU"
184 " GPLv2</a></p>")
185 .arg(VERSION)
186 .arg(QChar(0xA9)));
190 void
191 Main_GUI::browse_input()
193 // XXX remember last directory
194 QString file = QFileDialog::getOpenFileName(
195 this,
196 tr("Open Input File"),
197 QDir::homePath(),
198 "");
199 if (!file.isEmpty())
200 input_line->setText(QDir::toNativeSeparators(file));
204 void
205 Main_GUI::browse_output()
207 // XXX remember last directory
208 QString file = QFileDialog::getSaveFileName(
209 this,
210 tr("Open Output File"),
211 QDir::homePath(),
212 "");
214 if (!file.isEmpty())
215 output_line->setText(QDir::toNativeSeparators(file));
219 void
220 Main_GUI::browse_deltas()
222 // XXX remember last directory
223 QString file = QFileDialog::getOpenFileName(
224 this,
225 tr("Open Delta Exceptions File"),
226 QDir::homePath(),
227 "");
229 if (!file.isEmpty())
230 deltas_line->setText(QDir::toNativeSeparators(file));
234 void
235 Main_GUI::check_min()
237 int min = min_box->value();
238 int max = max_box->value();
239 int limit = limit_box->value();
240 if (min > max)
241 max_box->setValue(min);
242 if (min > limit)
243 limit_box->setValue(min);
247 void
248 Main_GUI::check_max()
250 int min = min_box->value();
251 int max = max_box->value();
252 int limit = limit_box->value();
253 if (max < min)
254 min_box->setValue(max);
255 if (max > limit)
256 limit_box->setValue(max);
260 void
261 Main_GUI::check_limit()
263 int min = min_box->value();
264 int max = max_box->value();
265 int limit = limit_box->value();
266 if (limit < max)
267 max_box->setValue(limit);
268 if (limit < min)
269 min_box->setValue(limit);
273 void
274 Main_GUI::check_dehint()
276 if (dehint_box->isChecked())
278 min_label->setEnabled(false);
279 min_box->setEnabled(false);
281 max_label->setEnabled(false);
282 max_box->setEnabled(false);
284 default_label->setEnabled(false);
285 default_box->setEnabled(false);
286 fallback_label->setEnabled(false);
287 fallback_box->setEnabled(false);
289 no_limit_box->setEnabled(false);
290 limit_label->setEnabled(false);
291 limit_box->setEnabled(false);
293 no_increase_box->setEnabled(false);
294 increase_label->setEnabled(false);
295 increase_box->setEnabled(false);
297 snapping_label->setEnabled(false);
298 snapping_line->setEnabled(false);
300 default_stem_width_box->setEnabled(false);
301 stem_width_label->setEnabled(false);
302 stem_width_box->setEnabled(false);
304 wincomp_box->setEnabled(false);
305 adjust_box->setEnabled(false);
306 hint_box->setEnabled(false);
307 symbol_box->setEnabled(false);
309 stem_label->setEnabled(false);
310 gray_box->setEnabled(false);
311 gdi_box->setEnabled(false);
312 dw_box->setEnabled(false);
314 else
316 min_label->setEnabled(true);
317 min_box->setEnabled(true);
319 max_label->setEnabled(true);
320 max_box->setEnabled(true);
322 default_label->setEnabled(true);
323 default_box->setEnabled(true);
324 fallback_label->setEnabled(true);
325 fallback_box->setEnabled(true);
327 no_limit_box->setEnabled(true);
328 check_no_limit();
330 no_increase_box->setEnabled(true);
331 check_no_increase();
333 snapping_label->setEnabled(true);
334 snapping_line->setEnabled(true);
336 default_stem_width_box->setEnabled(true);
337 check_default_stem_width();
339 wincomp_box->setEnabled(true);
340 adjust_box->setEnabled(true);
341 hint_box->setEnabled(true);
342 symbol_box->setEnabled(true);
344 stem_label->setEnabled(true);
345 gray_box->setEnabled(true);
346 gdi_box->setEnabled(true);
347 dw_box->setEnabled(true);
352 void
353 Main_GUI::check_no_limit()
355 if (no_limit_box->isChecked())
357 limit_label->setEnabled(false);
358 limit_label->setText(limit_label_text);
359 limit_box->setEnabled(false);
360 no_limit_box->setText(no_limit_box_text_with_key);
362 else
364 limit_label->setEnabled(true);
365 limit_label->setText(limit_label_text_with_key);
366 limit_box->setEnabled(true);
367 no_limit_box->setText(no_limit_box_text);
372 void
373 Main_GUI::check_no_increase()
375 if (no_increase_box->isChecked())
377 increase_label->setEnabled(false);
378 increase_label->setText(increase_label_text);
379 increase_box->setEnabled(false);
380 no_increase_box->setText(no_increase_box_text_with_key);
382 else
384 increase_label->setEnabled(true);
385 increase_label->setText(increase_label_text_with_key);
386 increase_box->setEnabled(true);
387 no_increase_box->setText(no_increase_box_text);
392 void
393 Main_GUI::check_default_stem_width()
395 if (default_stem_width_box->isChecked())
397 stem_width_label->setEnabled(false);
398 stem_width_label->setText(stem_width_label_text);
399 stem_width_box->setEnabled(false);
400 default_stem_width_box->setText(default_stem_width_box_text_with_key);
402 else
404 stem_width_label->setEnabled(true);
405 stem_width_label->setText(stem_width_label_text_with_key);
406 stem_width_box->setEnabled(true);
407 default_stem_width_box->setText(default_stem_width_box_text);
412 void
413 Main_GUI::check_run()
415 if (input_line->text().isEmpty() || output_line->text().isEmpty())
416 run_button->setEnabled(false);
417 else
418 run_button->setEnabled(true);
422 void
423 Main_GUI::absolute_input()
425 QString input_name = QDir::fromNativeSeparators(input_line->text());
426 if (!input_name.isEmpty()
427 && QDir::isRelativePath(input_name))
429 QDir cur_path(QDir::currentPath() + "/" + input_name);
430 input_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
435 void
436 Main_GUI::absolute_output()
438 QString output_name = QDir::fromNativeSeparators(output_line->text());
439 if (!output_name.isEmpty()
440 && QDir::isRelativePath(output_name))
442 QDir cur_path(QDir::currentPath() + "/" + output_name);
443 output_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
448 void
449 Main_GUI::absolute_deltas()
451 QString deltas_name = QDir::fromNativeSeparators(deltas_line->text());
452 if (!deltas_name.isEmpty()
453 && QDir::isRelativePath(deltas_name))
455 QDir cur_path(QDir::currentPath() + "/" + deltas_name);
456 deltas_line->setText(QDir::toNativeSeparators(cur_path.absolutePath()));
461 void
462 Main_GUI::check_number_set()
464 QString text = snapping_line->text();
465 QString qs;
467 // construct ASCII string from arbitrary Unicode data;
468 // the idea is to accept, say, CJK fullwidth digits also
469 for (int i = 0; i < text.size(); i++)
471 QChar c = text.at(i);
473 int digit = c.digitValue();
474 if (digit >= 0)
475 qs += QString::number(digit);
476 else if (c.isSpace())
477 qs += ' ';
478 // U+30FC KATAGANA-HIRAGANA PROLONGED SOUND MARK is assigned
479 // to the `-' key in some Japanese input methods
480 else if (c.category() == QChar::Punctuation_Dash
481 || c == QChar(0x30FC))
482 qs += '-';
483 // various Unicode COMMA characters,
484 // including representation forms
485 else if (c == QChar(',')
486 || c == QChar(0x055D)
487 || c == QChar(0x060C)
488 || c == QChar(0x07F8)
489 || c == QChar(0x1363)
490 || c == QChar(0x1802)
491 || c == QChar(0x1808)
492 || c == QChar(0x3001)
493 || c == QChar(0xA4FE)
494 || c == QChar(0xA60D)
495 || c == QChar(0xA6F5)
496 || c == QChar(0xFE10)
497 || c == QChar(0xFE11)
498 || c == QChar(0xFE50)
499 || c == QChar(0xFE51)
500 || c == QChar(0xFF0C)
501 || c == QChar(0xFF64))
502 qs += ',';
503 else
504 qs += c; // we do error handling below
507 if (x_height_snapping_exceptions)
508 number_set_free(x_height_snapping_exceptions);
510 QByteArray str = qs.toLocal8Bit();
511 const char* s = number_set_parse(str.constData(),
512 &x_height_snapping_exceptions,
513 6, 0x7FFF);
514 if (s && *s)
516 statusBar()->setStyleSheet("color: red;");
517 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
518 statusBar()->showMessage(
519 tr("allocation error"));
520 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_CHARACTER)
521 statusBar()->showMessage(
522 tr("invalid character (use digits, dashes, commas, and spaces)"));
523 else if (x_height_snapping_exceptions == NUMBERSET_OVERFLOW)
524 statusBar()->showMessage(
525 tr("overflow"));
526 else if (x_height_snapping_exceptions == NUMBERSET_INVALID_RANGE)
527 statusBar()->showMessage(
528 tr("invalid range (minimum is 6, maximum is 32767)"));
529 else if (x_height_snapping_exceptions == NUMBERSET_OVERLAPPING_RANGES)
530 statusBar()->showMessage(
531 tr("overlapping ranges"));
532 else if (x_height_snapping_exceptions == NUMBERSET_NOT_ASCENDING)
533 statusBar()->showMessage(
534 tr("values und ranges must be specified in ascending order"));
536 snapping_line->setText(qs);
537 snapping_line->setFocus(Qt::OtherFocusReason);
538 snapping_line->setCursorPosition(s - str.constData());
540 x_height_snapping_exceptions = NULL;
542 else
544 // normalize if there is no error
545 char* new_str = number_set_show(x_height_snapping_exceptions,
546 6, 0x7FFF);
547 snapping_line->setText(new_str);
548 free(new_str);
553 void
554 Main_GUI::clear_status_bar()
556 statusBar()->clearMessage();
557 statusBar()->setStyleSheet("");
562 Main_GUI::check_filenames(const QString& input_name,
563 const QString& output_name,
564 const QString& deltas_name)
566 if (!QFile::exists(input_name))
568 QMessageBox::warning(
569 this,
570 "TTFautohint",
571 tr("The file %1 cannot be found.")
572 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name))),
573 QMessageBox::Ok,
574 QMessageBox::Ok);
575 return 0;
578 if (input_name == output_name)
580 QMessageBox::warning(
581 this,
582 "TTFautohint",
583 tr("Input and output file names must be different."),
584 QMessageBox::Ok,
585 QMessageBox::Ok);
586 return 0;
589 // silently overwrite if watching is active
590 if (QFile::exists(output_name) && !timer->isActive())
592 int ret = QMessageBox::warning(
593 this,
594 "TTFautohint",
595 tr("The file %1 already exists.\n"
596 "Overwrite?")
597 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name))),
598 QMessageBox::Yes | QMessageBox::No,
599 QMessageBox::No);
600 if (ret == QMessageBox::No)
601 return 0;
604 if (!deltas_name.isEmpty() && !QFile::exists(deltas_name))
606 QMessageBox::warning(
607 this,
608 "TTFautohint",
609 tr("The file %1 cannot be found.")
610 .arg(QUOTE_STRING(QDir::toNativeSeparators(deltas_name))),
611 QMessageBox::Ok,
612 QMessageBox::Ok);
613 return 0;
616 return 1;
621 Main_GUI::open_files(const QString& input_name,
622 FILE** in,
623 const QString& output_name,
624 FILE** out,
625 const QString& deltas_name,
626 FILE** deltas)
628 const int buf_len = 1024;
629 char buf[buf_len];
631 *in = fopen(qPrintable(input_name), "rb");
632 if (!*in)
634 strerror_r(errno, buf, buf_len);
635 QMessageBox::warning(
636 this,
637 "TTFautohint",
638 tr("The following error occurred while opening input file %1:\n")
639 .arg(QUOTE_STRING(QDir::toNativeSeparators(input_name)))
640 + QString::fromLocal8Bit(buf),
641 QMessageBox::Ok,
642 QMessageBox::Ok);
643 return 0;
646 *out = fopen(qPrintable(output_name), "wb");
647 if (!*out)
649 strerror_r(errno, buf, buf_len);
650 QMessageBox::warning(
651 this,
652 "TTFautohint",
653 tr("The following error occurred while opening output file %1:\n")
654 .arg(QUOTE_STRING(QDir::toNativeSeparators(output_name)))
655 + QString::fromLocal8Bit(buf),
656 QMessageBox::Ok,
657 QMessageBox::Ok);
658 return 0;
661 if (!deltas_name.isEmpty())
663 *deltas = fopen(qPrintable(deltas_name), "r");
664 if (!*deltas)
666 strerror_r(errno, buf, buf_len);
667 QMessageBox::warning(
668 this,
669 "TTFautohint",
670 tr("The following error occurred"
671 " while opening delta exceptions file %1:\n")
672 .arg(QUOTE_STRING(QDir::toNativeSeparators(deltas_name)))
673 + QString::fromLocal8Bit(buf),
674 QMessageBox::Ok,
675 QMessageBox::Ok);
676 return 0;
680 return 1;
684 void
685 Main_GUI::check_watch()
687 if (!watch_box->isChecked())
688 timer->stop();
689 // the timer gets started in the `run' function
693 void
694 Main_GUI::watch_files()
696 if (fileinfo_input_file.exists()
697 && fileinfo_input_file.isReadable()
698 && fileinfo_deltas_file.exists()
699 && fileinfo_deltas_file.isReadable())
701 QDateTime modified_input = fileinfo_input_file.lastModified();
702 QDateTime modified_deltas = fileinfo_deltas_file.lastModified();
703 if (modified_input > datetime_input_file
704 || modified_deltas > datetime_deltas_file)
705 run(); // this function sets `datetime_XXX'
707 else
708 run(); // let this routine handle all errors
712 extern "C" {
714 struct GUI_Progress_Data
716 long last_sfnt;
717 bool begin;
718 QProgressDialog* dialog;
722 #undef TRDOMAIN
723 #define TRDOMAIN "GuiProgress"
726 gui_progress(long curr_idx,
727 long num_glyphs,
728 long curr_sfnt,
729 long num_sfnts,
730 void* user)
732 GUI_Progress_Data* data = static_cast<GUI_Progress_Data*>(user);
734 if (num_sfnts > 1 && curr_sfnt != data->last_sfnt)
736 data->dialog->setLabelText(Tr("Auto-hinting subfont %1 of %2"
737 " with %3 glyphs...")
738 .arg(curr_sfnt + 1)
739 .arg(num_sfnts)
740 .arg(num_glyphs));
742 if (curr_sfnt + 1 == num_sfnts)
744 data->dialog->setAutoReset(true);
745 data->dialog->setAutoClose(true);
747 else
749 data->dialog->setAutoReset(false);
750 data->dialog->setAutoClose(false);
753 data->last_sfnt = curr_sfnt;
754 data->begin = true;
757 if (data->begin)
759 if (num_sfnts == 1)
760 data->dialog->setLabelText(Tr("Auto-hinting %1 glyphs...")
761 .arg(num_glyphs));
762 data->dialog->setMaximum(num_glyphs - 1);
764 data->begin = false;
767 data->dialog->setValue(curr_idx);
769 if (data->dialog->wasCanceled())
770 return 1;
772 return 0;
776 struct GUI_Error_Data
778 Main_GUI* gui;
779 QLocale* locale;
780 QString output_name;
781 QString deltas_name;
782 int* ignore_restrictions_p;
783 bool retry;
787 #undef TRDOMAIN
788 #define TRDOMAIN "GuiError"
790 void
791 gui_error(TA_Error error,
792 const char* error_string,
793 unsigned int errlinenum,
794 const char* errline,
795 const char* errpos,
796 void* user)
798 GUI_Error_Data* data = static_cast<GUI_Error_Data*>(user);
799 QLocale* locale = data->locale; // for QUOTE_STRING_LITERAL
801 if (!error)
802 return;
804 if (error == TA_Err_Canceled)
806 else if (error == TA_Err_Invalid_FreeType_Version)
807 QMessageBox::critical(
808 data->gui,
809 "TTFautohint",
810 Tr("FreeType version 2.4.5 or higher is needed.\n"
811 "Are you perhaps using a wrong FreeType DLL?"),
812 QMessageBox::Ok,
813 QMessageBox::Ok);
814 else if (error == TA_Err_Invalid_Font_Type)
815 QMessageBox::warning(
816 data->gui,
817 "TTFautohint",
818 Tr("This font is not a valid font"
819 " in SFNT format with TrueType outlines.\n"
820 "In particular, CFF outlines are not supported."),
821 QMessageBox::Ok,
822 QMessageBox::Ok);
823 else if (error == TA_Err_Already_Processed)
824 QMessageBox::warning(
825 data->gui,
826 "TTFautohint",
827 Tr("This font has already been processed by TTFautohint."),
828 QMessageBox::Ok,
829 QMessageBox::Ok);
830 else if (error == TA_Err_Missing_Legal_Permission)
832 int yesno = QMessageBox::warning(
833 data->gui,
834 "TTFautohint",
835 Tr("Bit 1 in the %1 field of the %2 table is set:"
836 " This font must not be modified"
837 " without permission of the legal owner.\n"
838 "Do you have such a permission?")
839 .arg(QUOTE_STRING_LITERAL("fsType"))
840 .arg(QUOTE_STRING_LITERAL("OS/2")),
841 QMessageBox::Yes | QMessageBox::No,
842 QMessageBox::No);
843 if (yesno == QMessageBox::Yes)
845 *data->ignore_restrictions_p = true;
846 data->retry = true;
849 else if (error == TA_Err_Missing_Unicode_CMap)
850 QMessageBox::warning(
851 data->gui,
852 "TTFautohint",
853 Tr("The input font doesn't contain a Unicode character map.\n"
854 "Maybe you haven't set the %1 checkbox?")
855 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
856 QMessageBox::Ok,
857 QMessageBox::Ok);
858 else if (error == TA_Err_Missing_Symbol_CMap)
859 QMessageBox::warning(
860 data->gui,
861 "TTFautohint",
862 Tr("The input font does neither contain a symbol"
863 " nor a character map."),
864 QMessageBox::Ok,
865 QMessageBox::Ok);
866 else if (error == TA_Err_Missing_Glyph)
867 QMessageBox::warning(
868 data->gui,
869 "TTFautohint",
870 Tr("No glyph for a standard character"
871 " to derive standard width and height.\n"
872 "Please check the documentation for a list of"
873 " script-specific standard characters.\n"
874 "\n"
875 "Set the %1 checkbox if you want to circumvent this test.")
876 .arg(QUOTE_STRING_LITERAL(Tr("Symbol Font"))),
877 QMessageBox::Ok,
878 QMessageBox::Ok);
879 else if (error >= 0x200 && error < 0x300)
880 QMessageBox::warning(
881 data->gui,
882 "TTFautohint",
883 QString::fromLocal8Bit("%1:%2: %3 (0x%4)<br>"
884 "<tt> %5<br>"
885 " %6</tt>")
886 .arg(data->deltas_name)
887 .arg(errlinenum)
888 .arg(error_string)
889 .arg(error, 2, 16, QLatin1Char('0'))
890 .arg(errline)
891 .arg('^', int(errpos - errline + 1))
892 .replace(" ", "&nbsp;"),
893 QMessageBox::Ok,
894 QMessageBox::Ok);
895 else
896 QMessageBox::warning(
897 data->gui,
898 "TTFautohint",
899 Tr("Error code 0x%1 while autohinting font:\n")
900 .arg(error, 2, 16, QLatin1Char('0'))
901 + QString::fromLocal8Bit(error_string),
902 QMessageBox::Ok,
903 QMessageBox::Ok);
905 if (QFile::exists(data->output_name)
906 && remove(qPrintable(data->output_name)))
908 const int buf_len = 1024;
909 char buf[buf_len];
911 strerror_r(errno, buf, buf_len);
912 QMessageBox::warning(
913 data->gui,
914 "TTFautohint",
915 Tr("The following error occurred while removing output file %1:\n")
916 .arg(QUOTE_STRING(QDir::toNativeSeparators(data->output_name)))
917 + QString::fromLocal8Bit(buf),
918 QMessageBox::Ok,
919 QMessageBox::Ok);
923 } // extern "C"
926 void
927 Main_GUI::run()
929 statusBar()->clearMessage();
931 QString input_name = QDir::fromNativeSeparators(input_line->text());
932 QString output_name = QDir::fromNativeSeparators(output_line->text());
933 QString deltas_name = QDir::fromNativeSeparators(deltas_line->text());
934 if (!check_filenames(input_name, output_name, deltas_name))
936 timer->stop();
937 return;
940 // we need C file descriptors for communication with TTF_autohint
941 FILE* input;
942 FILE* output;
943 FILE* deltas;
945 again:
946 if (!open_files(input_name, &input,
947 output_name, &output,
948 deltas_name, &deltas))
950 timer->stop();
951 return;
954 QProgressDialog dialog;
955 dialog.setCancelButtonText(tr("Cancel"));
956 dialog.setMinimumDuration(1000);
957 dialog.setWindowModality(Qt::WindowModal);
959 TA_Info_Func info_func = info;
960 GUI_Progress_Data gui_progress_data = {-1, true, &dialog};
961 GUI_Error_Data gui_error_data = { this, locale, output_name, deltas_name,
962 &ignore_restrictions, false };
963 Info_Data info_data;
965 info_data.data = NULL; // must be deallocated after use
966 info_data.data_wide = NULL; // must be deallocated after use
967 info_data.data_len = 0;
968 info_data.data_wide_len = 0;
970 info_data.hinting_range_min = min_box->value();
971 info_data.hinting_range_max = max_box->value();
972 info_data.hinting_limit = no_limit_box->isChecked()
974 : limit_box->value();
976 info_data.gray_strong_stem_width = gray_box->isChecked();
977 info_data.gdi_cleartype_strong_stem_width = gdi_box->isChecked();
978 info_data.dw_cleartype_strong_stem_width = dw_box->isChecked();
980 info_data.increase_x_height = no_increase_box->isChecked()
982 : increase_box->value();
983 info_data.x_height_snapping_exceptions_string =
984 qPrintable(x_height_snapping_exceptions_string);
985 info_data.fallback_stem_width = default_stem_width_box->isChecked()
987 : stem_width_box->value();
989 info_data.windows_compatibility = wincomp_box->isChecked();
990 info_data.adjust_subglyphs = adjust_box->isChecked();
991 info_data.hint_composites = hint_box->isChecked();
992 info_data.symbol = symbol_box->isChecked();
993 info_data.dehint = dehint_box->isChecked();
995 strncpy(info_data.default_script,
996 script_names[default_box->currentIndex()].tag,
997 sizeof (info_data.default_script));
998 strncpy(info_data.fallback_script,
999 script_names[fallback_box->currentIndex()].tag,
1000 sizeof (info_data.fallback_script));
1002 if (info_box->isChecked())
1004 int ret = build_version_string(&info_data);
1005 if (ret == 1)
1006 QMessageBox::information(
1007 this,
1008 "TTFautohint",
1009 tr("Can't allocate memory for <b>TTFautohint</b> options string"
1010 " in <i>name</i> table."),
1011 QMessageBox::Ok,
1012 QMessageBox::Ok);
1013 else if (ret == 2)
1014 QMessageBox::information(
1015 this,
1016 "TTFautohint",
1017 tr("<b>TTFautohint</b> options string"
1018 " in <i>name</i> table too long."),
1019 QMessageBox::Ok,
1020 QMessageBox::Ok);
1022 else
1023 info_func = NULL;
1025 if (info_data.symbol
1026 && info_data.fallback_stem_width
1027 && !strcmp(info_data.fallback_script, "none"))
1028 QMessageBox::information(
1029 this,
1030 "TTFautohint",
1031 tr("Setting a fallback stem width for a symbol font"
1032 " without setting a fallback script has no effect."),
1033 QMessageBox::Ok,
1034 QMessageBox::Ok);
1036 fileinfo_input_file.setFile(input_name);
1037 fileinfo_deltas_file.setFile(deltas_name);
1038 datetime_input_file = fileinfo_input_file.lastModified();
1039 datetime_deltas_file = fileinfo_deltas_file.lastModified();
1041 QByteArray snapping_string = snapping_line->text().toLocal8Bit();
1043 TA_Error error =
1044 TTF_autohint("in-file, out-file, deltas-file,"
1045 "hinting-range-min, hinting-range-max,"
1046 "hinting-limit,"
1047 "gray-strong-stem-width,"
1048 "gdi-cleartype-strong-stem-width,"
1049 "dw-cleartype-strong-stem-width,"
1050 "progress-callback, progress-callback-data,"
1051 "error-callback, error-callback-data,"
1052 "info-callback, info-callback-data,"
1053 "ignore-restrictions,"
1054 "windows-compatibility,"
1055 "adjust-subglyphs,"
1056 "hint-composites,"
1057 "increase-x-height,"
1058 "x-height-snapping-exceptions, fallback-stem-width,"
1059 "default-script, fallback-script,"
1060 "symbol, dehint",
1061 input, output, deltas,
1062 info_data.hinting_range_min, info_data.hinting_range_max,
1063 info_data.hinting_limit,
1064 info_data.gray_strong_stem_width,
1065 info_data.gdi_cleartype_strong_stem_width,
1066 info_data.dw_cleartype_strong_stem_width,
1067 gui_progress, &gui_progress_data,
1068 gui_error, &gui_error_data,
1069 info_func, &info_data,
1070 ignore_restrictions,
1071 info_data.windows_compatibility,
1072 info_data.adjust_subglyphs,
1073 info_data.hint_composites,
1074 info_data.increase_x_height,
1075 snapping_string.constData(), info_data.fallback_stem_width,
1076 info_data.default_script, info_data.fallback_script,
1077 info_data.symbol, info_data.dehint);
1079 if (info_box->isChecked())
1081 free(info_data.data);
1082 free(info_data.data_wide);
1085 fclose(input);
1086 fclose(output);
1087 if (deltas)
1088 fclose(deltas);
1090 if (error)
1092 // retry if there is a user request to do so (handled in `gui_error')
1093 if (gui_error_data.retry)
1094 goto again;
1096 timer->stop();
1098 else
1100 statusBar()->showMessage(tr("Auto-hinting finished")
1101 + " ("
1102 + QDateTime::currentDateTime()
1103 .toString(Qt::TextDate)
1104 + ").");
1106 // we have successfully processed a file;
1107 // start file watching now if requested
1108 if (watch_box->isChecked())
1109 timer->start();
1114 // XXX distances are specified in pixels,
1115 // making the layout dependent on the output device resolution
1116 void
1117 Main_GUI::create_layout(bool horizontal_layout)
1120 // file stuff
1122 QCompleter* completer = new QCompleter(this);
1123 QFileSystemModel* model = new QFileSystemModel(completer);
1124 model->setRootPath(QDir::rootPath());
1125 completer->setModel(model);
1127 input_label = new QLabel(tr("&Input File:"));
1128 input_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1129 input_button = new QPushButton(tr("Browse..."));
1130 input_label->setBuddy(input_line);
1131 // enforce rich text to get nice word wrapping
1132 input_label->setToolTip(
1133 tr("<b></b>The input file, either a TrueType font (TTF),"
1134 " TrueType collection (TTC), or a TrueType-based OpenType font."));
1135 input_line->setCompleter(completer);
1137 output_label = new QLabel(tr("&Output File:"));
1138 output_line = new Drag_Drop_Line_Edit(DRAG_DROP_TRUETYPE);
1139 output_button = new QPushButton(tr("Browse..."));
1140 output_label->setBuddy(output_line);
1141 output_label->setToolTip(
1142 tr("<b></b>The output file, which will be essentially identical"
1143 " to the input font but will contain new, generated hints."));
1144 output_line->setCompleter(completer);
1146 deltas_label = new QLabel(tr("Delta &Exception File:"));
1147 deltas_line = new Drag_Drop_Line_Edit(DRAG_DROP_ANY);
1148 deltas_button = new QPushButton(tr("Browse..."));
1149 deltas_label->setBuddy(deltas_line);
1150 deltas_label->setToolTip(
1151 tr("<p>An optional delta exceptions file to fine-tune point positions"
1152 " after hinting, using DELTAP TrueType instructions."
1153 " This text file contains lines of the form<br>"
1154 "&nbsp;<br>"
1155 "&nbsp;&nbsp;[&nbsp;<i>subfont-idx</i>&nbsp;]"
1156 "&nbsp;&nbsp;<i>glyph-id</i>"
1157 "&nbsp;&nbsp;<tt>p</tt>&nbsp;<i>points</i>"
1158 "&nbsp;&nbsp;[&nbsp;<tt>x</tt>&nbsp;<i>shift</i>&nbsp;]"
1159 "&nbsp;&nbsp;[&nbsp;<tt>y</tt>&nbsp;<i>shift</i>&nbsp;]"
1160 "&nbsp;&nbsp;<tt>@</tt>&nbsp;<i>ppems</i><br>"
1161 "&nbsp;<br>"
1162 "<i>subfont-idx</i> gives the subfont index in a TTC,"
1163 " <i>glyph-id</i> is a glyph name or index,"
1164 " the x and y <i>shift</i> values are in the range [-1;1],"
1165 " rounded to multiples of 1/8px,"
1166 " <i>points</i> and <i>ppems</i> are ranges for point indices"
1167 " and ppem values as with x&nbsp;height snapping exceptions.<br>"
1168 "<tt>#</tt> starts a line comment, which gets ignored."
1169 " Empty lines are ignored, too.</p>"
1171 "Example:<br>"
1172 "&nbsp;&nbsp;<tt>Adieresis p 3-6 y 0.25 @ 13</tt>"));
1173 deltas_line->setCompleter(completer);
1176 // minmax controls
1178 min_label = new QLabel(tr("Hint Set Range Mi&nimum:"));
1179 min_box = new QSpinBox;
1180 min_label->setBuddy(min_box);
1181 min_label->setToolTip(
1182 tr("The minimum PPEM value of the range for which"
1183 " <b>TTFautohint</b> computes <i>hint sets</i>."
1184 " A hint set for a given PPEM value hints this size optimally."
1185 " The larger the range, the more hint sets are considered,"
1186 " usually increasing the size of the bytecode.<br>"
1187 "Note that changing this range doesn't influence"
1188 " the <i>gasp</i> table:"
1189 " Hinting is enabled for all sizes."));
1190 min_box->setKeyboardTracking(false);
1191 min_box->setRange(2, 10000);
1193 max_label = new QLabel(tr("Hint Set Range Ma&ximum:"));
1194 max_box = new QSpinBox;
1195 max_label->setBuddy(max_box);
1196 max_label->setToolTip(
1197 tr("The maximum PPEM value of the range for which"
1198 " <b>TTFautohint</b> computes <i>hint sets</i>."
1199 " A hint set for a given PPEM value hints this size optimally."
1200 " The larger the range, the more hint sets are considered,"
1201 " usually increasing the size of the bytecode.<br>"
1202 "Note that changing this range doesn't influence"
1203 " the <i>gasp</i> table:"
1204 " Hinting is enabled for all sizes."));
1205 max_box->setKeyboardTracking(false);
1206 max_box->setRange(2, 10000);
1209 // OpenType default script
1211 default_label = new QLabel(tr("Defa&ult Script:"));
1212 default_box = new QComboBox;
1213 default_label->setBuddy(default_box);
1214 default_label->setToolTip(
1215 tr("This sets the default script for OpenType features:"
1216 " After applying all features that are handled specially"
1217 " (for example small caps or superscript glyphs),"
1218 " <b>TTFautohint</b> uses this value for the remaining features."));
1219 for (int i = 0; script_names[i].tag; i++)
1221 // XXX: how to provide translations?
1222 default_box->insertItem(i,
1223 QString("%1 (%2)")
1224 .arg(script_names[i].tag)
1225 .arg(script_names[i].description));
1229 // hinting and fallback controls
1231 fallback_label = new QLabel(tr("Fallback &Script:"));
1232 fallback_box = new QComboBox;
1233 fallback_label->setBuddy(fallback_box);
1234 fallback_label->setToolTip(
1235 tr("This sets the fallback script for glyphs"
1236 " that <b>TTFautohint</b> can't map to a script automatically."));
1237 for (int i = 0; script_names[i].tag; i++)
1239 // XXX: how to provide translations?
1240 fallback_box->insertItem(i,
1241 QString("%1 (%2)")
1242 .arg(script_names[i].tag)
1243 .arg(script_names[i].description));
1247 // hinting limit
1249 limit_label_text_with_key = tr("Hinting &Limit:");
1250 limit_label_text = tr("Hinting Limit:");
1251 limit_label = new QLabel(limit_label_text_with_key);
1252 limit_box = new QSpinBox;
1253 limit_label->setBuddy(limit_box);
1254 limit_label->setToolTip(
1255 tr("Switch off hinting for PPEM values exceeding this limit."
1256 " Changing this value does not influence the size of the bytecode.<br>"
1257 "Note that <b>TTFautohint</b> handles this feature"
1258 " in the output font's bytecode and not in the <i>gasp</i> table."));
1259 limit_box->setKeyboardTracking(false);
1260 limit_box->setRange(2, 10000);
1262 no_limit_box_text_with_key = tr("No Hinting &Limit");
1263 no_limit_box_text = tr("No Hinting Limit");
1264 no_limit_box = new QCheckBox(no_limit_box_text, this);
1265 no_limit_box->setToolTip(
1266 tr("If switched on, <b>TTFautohint</b> adds no hinting limit"
1267 " to the bytecode.<br>"
1268 "For testing only."));
1271 // x height increase limit
1273 increase_label_text_with_key = tr("x Height In&crease Limit:");
1274 increase_label_text = tr("x Height Increase Limit:");
1275 increase_label = new QLabel(increase_label_text_with_key);
1276 increase_box = new QSpinBox;
1277 increase_label->setBuddy(increase_box);
1278 increase_label->setToolTip(
1279 tr("For PPEM values in the range 5&nbsp;&lt; PPEM &lt;&nbsp;<i>n</i>,"
1280 " where <i>n</i> is the value selected by this spin box,"
1281 " round up the font's x&nbsp;height much more often than normally.<br>"
1282 "Use this if holes in letters like <i>e</i> get filled,"
1283 " for example."));
1284 increase_box->setKeyboardTracking(false);
1285 increase_box->setRange(6, 10000);
1287 no_increase_box_text_with_key = tr("No x Height In&crease");
1288 no_increase_box_text = tr("No x Height Increase");
1289 no_increase_box = new QCheckBox(no_increase_box_text, this);
1290 no_increase_box->setToolTip(
1291 tr("If switched on,"
1292 " <b>TTFautohint</b> does not increase the x&nbsp;height."));
1295 // x height snapping exceptions
1297 snapping_label = new QLabel(tr("x Height Snapping Excep&tions:"));
1298 snapping_line = new Tooltip_Line_Edit;
1299 snapping_label->setBuddy(snapping_line);
1300 snapping_label->setToolTip(
1301 tr("<p>A list of comma separated PPEM values or value ranges"
1302 " at which no x&nbsp;height snapping shall be applied"
1303 " (x&nbsp;height snapping usually slightly increases"
1304 " the size of all glyphs).</p>"
1306 "Examples:<br>"
1307 "&nbsp;&nbsp;<tt>2, 3-5, 12-17</tt><br>"
1308 "&nbsp;&nbsp;<tt>-20, 40-</tt>"
1309 " (meaning PPEM &le; 20 or PPEM &ge; 40)<br>"
1310 "&nbsp;&nbsp;<tt>-</tt> (meaning all possible PPEM values)"));
1313 // fallback stem width
1315 stem_width_label_text_with_key = tr("Fall&back Stem Width:");
1316 stem_width_label_text = tr("Fallback Stem Width:");
1317 stem_width_label = new QLabel(stem_width_label_text_with_key);
1318 stem_width_box = new QSpinBox;
1319 stem_width_label->setBuddy(stem_width_box);
1320 stem_width_label->setToolTip(
1321 tr("Set horizontal stem width (in font units) for all scripts"
1322 " that lack proper standard characters in the font.<br>"
1323 "If not set, <b>TTFautohint</b> uses a hard-coded default value."));
1324 stem_width_box->setKeyboardTracking(false);
1325 stem_width_box->setRange(1, 10000);
1327 default_stem_width_box_text_with_key = tr("Default Fall&back Stem Width");
1328 default_stem_width_box_text = tr("Default Fallback Stem Width");
1329 default_stem_width_box = new QCheckBox(default_stem_width_box_text, this);
1330 default_stem_width_box->setToolTip(
1331 tr("If switched on, <b>TTFautohint</b> uses a default value"
1332 " for the fallback stem width (50 font units at 2048 UPEM)."));
1335 // flags
1337 wincomp_box = new QCheckBox(tr("Windows Com&patibility"), this);
1338 wincomp_box->setToolTip(
1339 tr("If switched on, add two artificial blue zones positioned at the"
1340 " <tt>usWinAscent</tt> and <tt>usWinDescent</tt> values"
1341 " (from the font's <i>OS/2</i> table)."
1342 " This option, usually in combination"
1343 " with value <tt>-</tt> (a single dash)"
1344 " for the <i>x&nbsp;Height Snapping Exceptions</i> option,"
1345 " should be used if those two <i>OS/2</i> values are tight,"
1346 " and you are experiencing clipping during rendering."));
1348 adjust_box = new QCheckBox(tr("Ad&just Subglyphs"), this);
1349 adjust_box->setToolTip(
1350 tr("If switched on, the original bytecode of the input font"
1351 " gets applied (at EM size, usually 2048ppem)"
1352 " to derive the glyph outlines for <b>TTFautohint</b>.<br>"
1353 "Use this option only if subglyphs"
1354 " are incorrectly scaled and shifted.<br>"
1355 "Note that the original bytecode will always be discarded."));
1357 hint_box = new QCheckBox(tr("Hint Co&mposites")
1358 + " ", this); // make label wider
1359 hint_box->setToolTip(
1360 tr("If switched on, <b>TTFautohint</b> hints composite glyphs"
1361 " as a whole, including subglyphs."
1362 " Otherwise, glyph components get hinted separately.<br>"
1363 "Deactivating this flag reduces the bytecode size enormously,"
1364 " however, it might yield worse results."));
1366 symbol_box = new QCheckBox(tr("S&ymbol Font"), this);
1367 symbol_box->setToolTip(
1368 tr("If switched on, <b>TTFautohint</b> accepts fonts"
1369 " that don't contain a single standard character"
1370 " for any of the supported scripts.<br>"
1371 "Use this for symbol or dingbat fonts, for example."));
1373 dehint_box = new QCheckBox(tr("&Dehint"), this);
1374 dehint_box->setToolTip(
1375 tr("If set, remove all hints from the font.<br>"
1376 "For testing only."));
1378 info_box = new QCheckBox(tr("Add ttf&autohint Info"), this);
1379 info_box->setToolTip(
1380 tr("If switched on, information about <b>TTFautohint</b>"
1381 " and its calling parameters are added to the version string(s)"
1382 " (name ID&nbsp;5) in the <i>name</i> table."));
1385 // stem width and positioning
1387 stem_label = new QLabel(tr("Stron&g Stem Width and Positioning:"));
1388 stem_label->setToolTip(
1389 tr("<b>TTFautohint</b> provides two different hinting algorithms"
1390 " that can be selected for various hinting modes."
1392 "<p><i>strong</i> (checkbox set):"
1393 " Position horizontal stems and snap stem widths"
1394 " to integer pixel values. While making the output look crisper,"
1395 " outlines become more distorted.</p>"
1397 "<p><i>smooth</i> (checkbox not set):"
1398 " Use discrete values for horizontal stems and stem widths."
1399 " This only slightly increases the contrast"
1400 " but avoids larger outline distortion.</p>"));
1402 gray_box = new QCheckBox(tr("Grayscale"), this);
1403 gray_box->setToolTip(
1404 tr("<b></b>Grayscale rendering, no ClearType activated."));
1405 stem_label->setBuddy(gray_box);
1407 gdi_box = new QCheckBox(tr("GDI ClearType"), this);
1408 gdi_box->setToolTip(
1409 tr("GDI ClearType rendering,"
1410 " introduced in 2000 for Windows XP.<br>"
1411 "The rasterizer version (as returned by the"
1412 " GETINFO bytecode instruction) is in the range"
1413 " 36&nbsp;&le; version &lt;&nbsp;38, and ClearType is enabled.<br>"
1414 "Along the vertical axis, this mode behaves like B/W rendering."));
1416 dw_box = new QCheckBox(tr("DW ClearType"), this);
1417 dw_box->setToolTip(
1418 tr("DirectWrite ClearType rendering,"
1419 " introduced in 2008 for Windows Vista.<br>"
1420 "The rasterizer version (as returned by the"
1421 " GETINFO bytecode instruction) is &ge;&nbsp;38,"
1422 " ClearType is enabled, and subpixel positioning is enabled also.<br>"
1423 "Smooth rendering along the vertical axis."));
1426 // running
1428 watch_box = new QCheckBox(tr("&Watch Input Files"), this);
1429 watch_box->setToolTip(
1430 tr("If switched on, <b>TTFautohint</b> automatically re-runs"
1431 " the hinting process as soon as an input file"
1432 " (either the font or the delta exceptions file) is modified.<br>"
1433 "Pressing the %1 button starts watching.<br>"
1434 "If an error occurs, watching stops and must be restarted"
1435 " with the %1 button.")
1436 .arg(QUOTE_STRING_LITERAL(tr("Run"))));
1438 run_button = new QPushButton(" "
1439 + tr("&Run")
1440 + " "); // make label wider
1442 if (horizontal_layout)
1443 create_horizontal_layout();
1444 else
1445 create_vertical_layout();
1449 // XXX distances are specified in pixels,
1450 // making the layout dependent on the output device resolution
1451 void
1452 Main_GUI::create_vertical_layout()
1454 // top area
1455 QGridLayout* file_layout = new QGridLayout;
1457 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1458 file_layout->addWidget(input_line, 0, 1);
1459 file_layout->addWidget(input_button, 0, 2);
1461 file_layout->setRowStretch(1, 1);
1463 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1464 file_layout->addWidget(output_line, 2, 1);
1465 file_layout->addWidget(output_button, 2, 2);
1467 file_layout->setRowStretch(3, 1);
1469 file_layout->addWidget(deltas_label, 4, 0, Qt::AlignRight);
1470 file_layout->addWidget(deltas_line, 4, 1);
1471 file_layout->addWidget(deltas_button, 4, 2);
1473 // bottom area
1474 QGridLayout* run_layout = new QGridLayout;
1476 run_layout->addWidget(watch_box, 0, 1);
1477 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1478 run_layout->setColumnStretch(0, 1);
1479 run_layout->setColumnStretch(2, 2);
1482 // the whole gui
1484 QGridLayout* gui_layout = new QGridLayout;
1485 QFrame* hline = new QFrame;
1486 hline->setFrameShape(QFrame::HLine);
1487 int row = 0; // this counter simplifies inserting new items
1489 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1490 gui_layout->setRowStretch(row++, 1);
1492 gui_layout->addLayout(file_layout, row, 0, row, -1);
1493 gui_layout->setRowStretch(row++, 1);
1495 gui_layout->addWidget(hline, row, 0, row, -1);
1496 gui_layout->setRowStretch(row++, 1);
1498 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1499 gui_layout->setRowStretch(row++, 1);
1501 gui_layout->addWidget(min_label, row, 0, Qt::AlignRight);
1502 gui_layout->addWidget(min_box, row++, 1, Qt::AlignLeft);
1503 gui_layout->addWidget(max_label, row, 0, Qt::AlignRight);
1504 gui_layout->addWidget(max_box, row++, 1, Qt::AlignLeft);
1506 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1507 gui_layout->setRowStretch(row++, 1);
1509 gui_layout->addWidget(default_label, row, 0, Qt::AlignRight);
1510 gui_layout->addWidget(default_box, row++, 1, Qt::AlignLeft);
1511 gui_layout->addWidget(fallback_label, row, 0, Qt::AlignRight);
1512 gui_layout->addWidget(fallback_box, row++, 1, Qt::AlignLeft);
1514 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1515 gui_layout->setRowStretch(row++, 1);
1517 gui_layout->addWidget(limit_label, row, 0, Qt::AlignRight);
1518 gui_layout->addWidget(limit_box, row++, 1, Qt::AlignLeft);
1519 gui_layout->addWidget(no_limit_box, row++, 1);
1521 gui_layout->addWidget(increase_label, row, 0, Qt::AlignRight);
1522 gui_layout->addWidget(increase_box, row++, 1, Qt::AlignLeft);
1523 gui_layout->addWidget(no_increase_box, row++, 1);
1525 gui_layout->addWidget(snapping_label, row, 0, Qt::AlignRight);
1526 gui_layout->addWidget(snapping_line, row++, 1, Qt::AlignLeft);
1528 gui_layout->addWidget(stem_width_label, row, 0, Qt::AlignRight);
1529 gui_layout->addWidget(stem_width_box, row++, 1, Qt::AlignLeft);
1530 gui_layout->addWidget(default_stem_width_box, row++, 1);
1532 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1533 gui_layout->setRowStretch(row++, 1);
1535 gui_layout->addWidget(wincomp_box, row++, 1);
1536 gui_layout->addWidget(adjust_box, row++, 1);
1537 gui_layout->addWidget(hint_box, row++, 1);
1538 gui_layout->addWidget(symbol_box, row++, 1);
1539 gui_layout->addWidget(dehint_box, row++, 1);
1540 gui_layout->addWidget(info_box, row++, 1);
1542 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1543 gui_layout->setRowStretch(row++, 1);
1545 gui_layout->addWidget(stem_label, row, 0, Qt::AlignRight);
1546 gui_layout->addWidget(gray_box, row++, 1);
1547 gui_layout->addWidget(gdi_box, row++, 1);
1548 gui_layout->addWidget(dw_box, row++, 1);
1550 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1551 gui_layout->setRowStretch(row++, 1);
1553 gui_layout->addLayout(run_layout, row, 0, row, -1);
1555 // create dummy widget to register layout
1556 QWidget* main_widget = new QWidget;
1557 main_widget->setLayout(gui_layout);
1558 setCentralWidget(main_widget);
1559 setWindowTitle("TTFautohint");
1563 // XXX distances are specified in pixels,
1564 // making the layout dependent on the output device resolution
1565 void
1566 Main_GUI::create_horizontal_layout()
1568 // top area
1569 QGridLayout* file_layout = new QGridLayout;
1571 file_layout->addWidget(input_label, 0, 0, Qt::AlignRight);
1572 file_layout->addWidget(input_line, 0, 1);
1573 file_layout->addWidget(input_button, 0, 2);
1575 file_layout->setRowStretch(1, 1);
1577 file_layout->addWidget(output_label, 2, 0, Qt::AlignRight);
1578 file_layout->addWidget(output_line, 2, 1);
1579 file_layout->addWidget(output_button, 2, 2);
1581 file_layout->setRowStretch(3, 1);
1583 file_layout->addWidget(deltas_label, 4, 0, Qt::AlignRight);
1584 file_layout->addWidget(deltas_line, 4, 1);
1585 file_layout->addWidget(deltas_button, 4, 2);
1587 // bottom area
1588 QGridLayout* run_layout = new QGridLayout;
1590 run_layout->addWidget(watch_box, 0, 1);
1591 run_layout->addWidget(run_button, 0, 3, Qt::AlignRight);
1592 run_layout->setColumnStretch(0, 2);
1593 run_layout->setColumnStretch(2, 3);
1594 run_layout->setColumnStretch(4, 1);
1597 // the whole gui
1599 QGridLayout* gui_layout = new QGridLayout;
1600 QFrame* hline = new QFrame;
1601 hline->setFrameShape(QFrame::HLine);
1602 int row = 0; // this counter simplifies inserting new items
1604 // margin
1605 gui_layout->setColumnMinimumWidth(0, 10); // XXX urgh, pixels...
1606 gui_layout->setColumnStretch(0, 1);
1608 // left
1609 gui_layout->setRowMinimumHeight(row, 10); // XXX urgh, pixels...
1610 gui_layout->setRowStretch(row++, 1);
1612 gui_layout->addLayout(file_layout, row, 0, row, -1);
1613 gui_layout->setRowStretch(row++, 1);
1615 gui_layout->addWidget(hline, row, 0, row, -1);
1616 gui_layout->setRowStretch(row++, 1);
1618 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1619 gui_layout->setRowStretch(row++, 1);
1621 gui_layout->addWidget(min_label, row, 1, Qt::AlignRight);
1622 gui_layout->addWidget(min_box, row++, 2, Qt::AlignLeft);
1623 gui_layout->addWidget(max_label, row, 1, Qt::AlignRight);
1624 gui_layout->addWidget(max_box, row++, 2, Qt::AlignLeft);
1626 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1627 gui_layout->setRowStretch(row++, 1);
1629 gui_layout->addWidget(default_label, row, 1, Qt::AlignRight);
1630 gui_layout->addWidget(default_box, row++, 2, Qt::AlignLeft);
1631 gui_layout->addWidget(fallback_label, row, 1, Qt::AlignRight);
1632 gui_layout->addWidget(fallback_box, row++, 2, Qt::AlignLeft);
1634 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1635 gui_layout->setRowStretch(row++, 1);
1637 gui_layout->addWidget(limit_label, row, 1, Qt::AlignRight);
1638 gui_layout->addWidget(limit_box, row++, 2, Qt::AlignLeft);
1639 gui_layout->addWidget(no_limit_box, row++, 2);
1641 gui_layout->addWidget(increase_label, row, 1, Qt::AlignRight);
1642 gui_layout->addWidget(increase_box, row++, 2, Qt::AlignLeft);
1643 gui_layout->addWidget(no_increase_box, row++, 2);
1645 gui_layout->addWidget(snapping_label, row, 1, Qt::AlignRight);
1646 gui_layout->addWidget(snapping_line, row++, 2, Qt::AlignLeft);
1648 gui_layout->addWidget(stem_width_label, row, 1, Qt::AlignRight);
1649 gui_layout->addWidget(stem_width_box, row++, 2, Qt::AlignLeft);
1650 gui_layout->addWidget(default_stem_width_box, row++, 2);
1652 gui_layout->setRowMinimumHeight(row, 30); // XXX urgh, pixels...
1653 gui_layout->setRowStretch(row++, 1);
1655 gui_layout->addLayout(run_layout, row, 0, row, -1);
1657 // column separator
1658 gui_layout->setColumnMinimumWidth(3, 20); // XXX urgh, pixels...
1659 gui_layout->setColumnStretch(3, 1);
1661 // right
1662 row = 4;
1663 gui_layout->addWidget(wincomp_box, row++, 4);
1664 gui_layout->addWidget(adjust_box, row++, 4);
1665 gui_layout->addWidget(hint_box, row++, 4);
1666 gui_layout->addWidget(symbol_box, row++, 4);
1667 gui_layout->addWidget(dehint_box, row++, 4);
1668 gui_layout->addWidget(info_box, row++, 4);
1670 gui_layout->setRowMinimumHeight(row, 20); // XXX urgh, pixels...
1671 gui_layout->setRowStretch(row++, 1);
1673 gui_layout->addWidget(stem_label, row++, 4);
1675 QGridLayout* stem_layout = new QGridLayout;
1676 stem_layout->setColumnMinimumWidth(0, 20); // XXX urgh, pixels...
1677 stem_layout->addWidget(gray_box, 0, 1);
1678 stem_layout->addWidget(gdi_box, 1, 1);
1679 stem_layout->addWidget(dw_box, 2, 1);
1681 gui_layout->addLayout(stem_layout, row, 4, 3, 1);
1682 row += 3;
1684 // margin
1685 gui_layout->setColumnMinimumWidth(5, 10); // XXX urgh, pixels...
1686 gui_layout->setColumnStretch(5, 1);
1688 // create dummy widget to register layout
1689 QWidget* main_widget = new QWidget;
1690 main_widget->setLayout(gui_layout);
1691 setCentralWidget(main_widget);
1692 setWindowTitle("TTFautohint");
1696 void
1697 Main_GUI::create_connections()
1699 connect(input_button, SIGNAL(clicked()), this,
1700 SLOT(browse_input()));
1701 connect(output_button, SIGNAL(clicked()), this,
1702 SLOT(browse_output()));
1703 connect(deltas_button, SIGNAL(clicked()), this,
1704 SLOT(browse_deltas()));
1706 connect(input_line, SIGNAL(textChanged(QString)), this,
1707 SLOT(check_run()));
1708 connect(output_line, SIGNAL(textChanged(QString)), this,
1709 SLOT(check_run()));
1711 connect(input_line, SIGNAL(editingFinished()), this,
1712 SLOT(absolute_input()));
1713 connect(output_line, SIGNAL(editingFinished()), this,
1714 SLOT(absolute_output()));
1715 connect(deltas_line, SIGNAL(editingFinished()), this,
1716 SLOT(absolute_deltas()));
1718 connect(min_box, SIGNAL(valueChanged(int)), this,
1719 SLOT(check_min()));
1720 connect(max_box, SIGNAL(valueChanged(int)), this,
1721 SLOT(check_max()));
1723 connect(limit_box, SIGNAL(valueChanged(int)), this,
1724 SLOT(check_limit()));
1725 connect(no_limit_box, SIGNAL(clicked()), this,
1726 SLOT(check_no_limit()));
1728 connect(no_increase_box, SIGNAL(clicked()), this,
1729 SLOT(check_no_increase()));
1731 connect(snapping_line, SIGNAL(editingFinished()), this,
1732 SLOT(check_number_set()));
1733 connect(snapping_line, SIGNAL(textEdited(QString)), this,
1734 SLOT(clear_status_bar()));
1736 connect(default_stem_width_box, SIGNAL(clicked()), this,
1737 SLOT(check_default_stem_width()));
1739 connect(dehint_box, SIGNAL(clicked()), this,
1740 SLOT(check_dehint()));
1742 connect(timer, SIGNAL(timeout()), this,
1743 SLOT(watch_files()));
1745 connect(watch_box, SIGNAL(clicked()), this,
1746 SLOT(check_watch()));
1748 connect(run_button, SIGNAL(clicked()), this,
1749 SLOT(run()));
1753 void
1754 Main_GUI::create_actions()
1756 exit_act = new QAction(tr("E&xit"), this);
1757 exit_act->setShortcuts(QKeySequence::Quit);
1758 connect(exit_act, SIGNAL(triggered()), this, SLOT(close()));
1760 about_act = new QAction(tr("&About"), this);
1761 connect(about_act, SIGNAL(triggered()), this, SLOT(about()));
1763 about_Qt_act = new QAction(tr("About &Qt"), this);
1764 connect(about_Qt_act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1768 void
1769 Main_GUI::create_menus()
1771 file_menu = menuBar()->addMenu(tr("&File"));
1772 file_menu->addAction(exit_act);
1774 help_menu = menuBar()->addMenu(tr("&Help"));
1775 help_menu->addAction(about_act);
1776 help_menu->addAction(about_Qt_act);
1780 void
1781 Main_GUI::create_status_bar()
1783 statusBar()->showMessage("");
1787 void
1788 Main_GUI::set_defaults()
1790 min_box->setValue(hinting_range_min);
1791 max_box->setValue(hinting_range_max);
1793 default_box->setCurrentIndex(default_script_idx);
1794 fallback_box->setCurrentIndex(fallback_script_idx);
1796 limit_box->setValue(hinting_limit ? hinting_limit : hinting_range_max);
1797 // handle command line option `--hinting-limit=0'
1798 if (!hinting_limit)
1800 hinting_limit = max_box->value();
1801 no_limit_box->setChecked(true);
1804 increase_box->setValue(increase_x_height ? increase_x_height
1805 : TA_INCREASE_X_HEIGHT);
1806 // handle command line option `--increase-x-height=0'
1807 if (!increase_x_height)
1809 increase_x_height = TA_INCREASE_X_HEIGHT;
1810 no_increase_box->setChecked(true);
1813 snapping_line->setText(x_height_snapping_exceptions_string);
1815 if (fallback_stem_width)
1816 stem_width_box->setValue(fallback_stem_width);
1817 else
1819 stem_width_box->setValue(50);
1820 default_stem_width_box->setChecked(true);
1823 if (windows_compatibility)
1824 wincomp_box->setChecked(true);
1825 if (adjust_subglyphs)
1826 adjust_box->setChecked(true);
1827 if (hint_composites)
1828 hint_box->setChecked(true);
1829 if (symbol)
1830 symbol_box->setChecked(true);
1831 if (dehint)
1832 dehint_box->setChecked(true);
1833 if (!no_info)
1834 info_box->setChecked(true);
1836 if (gray_strong_stem_width)
1837 gray_box->setChecked(true);
1838 if (gdi_cleartype_strong_stem_width)
1839 gdi_box->setChecked(true);
1840 if (dw_cleartype_strong_stem_width)
1841 dw_box->setChecked(true);
1843 run_button->setEnabled(false);
1845 check_min();
1846 check_max();
1847 check_limit();
1849 check_no_limit();
1850 check_no_increase();
1851 check_number_set();
1853 // do this last since it disables almost everything
1854 check_dehint();
1858 void
1859 Main_GUI::read_settings()
1861 QSettings settings;
1862 // QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1863 // QSize size = settings.value("size", QSize(400, 400)).toSize();
1864 // resize(size);
1865 // move(pos);
1869 void
1870 Main_GUI::write_settings()
1872 QSettings settings;
1873 // settings.setValue("pos", pos());
1874 // settings.setValue("size", size());
1877 // end of maingui.cpp