Commit 7e8950c8 authored by Piotr Drąg's avatar Piotr Drąg 🌻
Browse files

Remove duplicate font-view.c in top directory

Accidentally committed in 5eabb1f7.

https://bugzilla.gnome.org/show_bug.cgi?id=787759
parent 28c9ce77
/* -*- mode: C; c-basic-offset: 4 -*- */
/*
* font-view: a font viewer for GNOME
*
* Copyright (C) 2002-2003 James Henstridge <james@daa.com.au>
* Copyright (C) 2010 Cosimo Cecchi <cosimoc@gnome.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TYPE1_TABLES_H
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H
#include FT_MULTIPLE_MASTERS_H
#include <cairo/cairo-ft.h>
#include <fontconfig/fontconfig.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <hb.h>
#include <hb-ot.h>
#include <hb-ft.h>
#include "font-model.h"
#include "sushi-font-widget.h"
#define FONT_VIEW_TYPE_APPLICATION font_view_application_get_type()
#define FONT_VIEW_APPLICATION(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), FONT_VIEW_TYPE_APPLICATION, FontViewApplication))
typedef struct {
GtkApplication parent;
GtkWidget *main_window;
GtkWidget *main_grid;
GtkWidget *header;
GtkWidget *title_label;
GtkWidget *side_grid;
GtkWidget *font_widget;
GtkWidget *info_button;
GtkWidget *install_button;
GtkWidget *back_button;
GtkWidget *stack;
GtkWidget *swin_view;
GtkWidget *swin_preview;
GtkWidget *icon_view;
GtkTreeModel *model;
GFile *font_file;
} FontViewApplication;
typedef struct {
GtkApplicationClass parent_class;
} FontViewApplicationClass;
static gboolean
_print_version_and_exit (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
g_print("%s %s\n", _("GNOME Fonts"), VERSION);
exit (EXIT_SUCCESS);
return TRUE;
}
static const GOptionEntry goption_options[] =
{
{ "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
_print_version_and_exit, N_("Show the application's version"), NULL},
{ NULL }
};
G_DEFINE_TYPE (FontViewApplication, font_view_application, GTK_TYPE_APPLICATION);
static void font_view_application_do_overview (FontViewApplication *self);
static void ensure_window (FontViewApplication *self);
#define VIEW_ITEM_WIDTH 140
#define VIEW_ITEM_WRAP_WIDTH 128
#define VIEW_COLUMN_SPACING 36
#define VIEW_MARGIN 16
#define WHITESPACE_CHARS "\f \t"
static void
strip_whitespace (gchar **original)
{
GString *reassembled;
gchar **split;
const gchar *str;
gint idx, n_stripped;
size_t len;
split = g_strsplit (*original, "\n", -1);
reassembled = g_string_new (NULL);
n_stripped = 0;
for (idx = 0; split[idx] != NULL; idx++) {
str = split[idx];
len = strspn (str, WHITESPACE_CHARS);
if (len)
str += len;
if (strlen (str) == 0 &&
((split[idx + 1] == NULL) || strlen (split[idx + 1]) == 0))
continue;
if (n_stripped++ > 0)
g_string_append (reassembled, "\n");
g_string_append (reassembled, str);
}
g_strfreev (split);
g_free (*original);
*original = g_string_free (reassembled, FALSE);
}
#define MATCH_VERSION_STR "Version"
static void
strip_version (gchar **original)
{
gchar *ptr, *stripped;
ptr = g_strstr_len (*original, -1, MATCH_VERSION_STR);
if (!ptr)
return;
ptr += strlen (MATCH_VERSION_STR);
stripped = g_strdup (ptr);
strip_whitespace (&stripped);
g_free (*original);
*original = stripped;
}
static void
add_row (GtkWidget *grid,
const gchar *name,
const gchar *value,
gboolean multiline)
{
GtkWidget *name_w, *label;
name_w = gtk_label_new (name);
gtk_style_context_add_class (gtk_widget_get_style_context (name_w), "dim-label");
gtk_widget_set_halign (name_w, GTK_ALIGN_END);
gtk_widget_set_valign (name_w, GTK_ALIGN_START);
gtk_container_add (GTK_CONTAINER (grid), name_w);
label = gtk_label_new (value);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_label_set_selectable (GTK_LABEL(label), TRUE);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
if (multiline && g_utf8_strlen (value, -1) > 64) {
gtk_label_set_width_chars (GTK_LABEL (label), 64);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
}
gtk_label_set_max_width_chars (GTK_LABEL (label), 64);
gtk_grid_attach_next_to (GTK_GRID (grid), label,
name_w, GTK_POS_RIGHT,
1, 1);
}
#define FixedToFloat(f) (((float)(f))/65536.0)
static char *
describe_axis (FT_Var_Axis *ax)
{
return g_strdup_printf (_("%s [%g, %g], default %g"), ax->name,
FixedToFloat(ax->minimum),
FixedToFloat(ax->maximum),
FixedToFloat(ax->def));
}
static char *
get_sfnt_name (FT_Face face, guint id)
{
guint count, i;
count = FT_Get_Sfnt_Name_Count (face);
for (i = 0; i < count; i++) {
FT_SfntName sname;
if (FT_Get_Sfnt_Name (face, i, &sname) != 0)
continue;
if (sname.name_id != id)
continue;
/* only handle the unicode names for US langid */
if (!(sname.platform_id == TT_PLATFORM_MICROSOFT &&
sname.encoding_id == TT_MS_ID_UNICODE_CS &&
sname.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES))
continue;
return g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
}
return NULL;
}
static gboolean
is_valid_subfamily_id (guint id)
{
return id == 2 || id == 17 || (255 < id && id < 32768);
}
static void
describe_instance (FT_Face face, FT_Var_Named_Style *ns, int pos, GString *s)
{
if (is_valid_subfamily_id (ns->strid)) {
char *str = get_sfnt_name (face, ns->strid);
if (str) {
if (s->len > 0)
g_string_append (s, ", ");
g_string_append (s, str);
g_free (str);
return;
}
}
if (s->len > 0)
g_string_append (s, ", ");
g_string_append_printf (s, _("Instance %d"), pos);
}
static struct {
const char *tag;
const char *name;
} features[] = {
{ "kern", N_("Kerning") },
{ "liga", N_("Common Ligatures") },
{ "dlig", N_("Discretionary Ligatures") },
{ "hlig", N_("Historical Ligatures") },
{ "clig", N_("Contextual Ligatures") },
{ "smcp", N_("Small Caps") },
{ "c2sc", N_("Small Caps from Caps") },
{ "pcap", N_("Petite Caps") },
{ "c2pc", N_("Caps to Petite Caps") },
{ "unic", N_("Unicase") },
{ "cpsp", N_("Capital Spacing") },
{ "case", N_("Case-sensitive Forms") },
{ "lnum", N_("Lining Numbers") },
{ "onum", N_("Old-Style Numbers") },
{ "pnum", N_("Proportional Numbers") },
{ "tnum", N_("Tabular Numbers") },
{ "frac", N_("Normal Fractions") },
{ "afrc", N_("Alternate Fractions") },
{ "zero", N_("Slashed Zero") },
{ "nalt", N_("Alternative Annotations") },
{ "sinf", N_("Scientific Inferiors") },
{ "swsh", N_("Swash Glyphs") },
{ "cswh", N_("Contextual Swash") },
{ "locl", N_("Localized Forms") },
{ "calt", N_("Contextual Alternatives") },
{ "hist", N_("Historical Alternatives") },
{ "salt", N_("Stylistic Alternatives") },
{ "titl", N_("Titling Alternatives") },
{ "rand", N_("Randomize") },
{ "subs", N_("Subscript") },
{ "sups", N_("Superscript") },
{ "init", N_("Initial Forms") },
{ "medi", N_("Medial Forms") },
{ "fina", N_("Final Forms") },
{ "isol", N_("Isolated Forms") },
{ "ss01", N_("Stylistic Set 1") },
{ "ss02", N_("Stylistic Set 2") },
{ "ss03", N_("Stylistic Set 3") },
{ "ss04", N_("Stylistic Set 4") },
{ "ss05", N_("Stylistic Set 5") },
};
static char *
get_features (FT_Face face)
{
hb_font_t *hb_font;
int i, j;
GString *s;
s = g_string_new ("");
hb_font = hb_ft_font_create (face, NULL);
if (hb_font) {
hb_tag_t tables[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
hb_face_t *hb_face;
hb_face = hb_font_get_face (hb_font);
for (i = 0; i < 2; i++) {
hb_tag_t features[80];
unsigned int count = G_N_ELEMENTS (features);
unsigned int script_index = 0;
unsigned int lang_index = 0;
hb_ot_layout_language_get_feature_tags (hb_face,
tables[i],
script_index,
lang_index,
0,
&count,
features);
for (j = 0; j < count; j++) {
char buf[5];
hb_tag_to_string (features[j], buf); buf[4] = '\0';
if (s->len > 0)
g_string_append (s, ", ");
g_string_append (s, buf);
}
}
}
if (s->len > 0)
return g_string_free (s, FALSE);
g_string_free (s, TRUE);
return NULL;
}
static void
populate_grid (FontViewApplication *self,
GtkWidget *grid,
FT_Face face)
{
gchar *s;
GFileInfo *info;
PS_FontInfoRec ps_info;
FT_MM_Var *ft_mm_var;
add_row (grid, _("Name"), face->family_name, FALSE);
if (face->style_name)
add_row (grid, _("Style"), face->style_name, FALSE);
info = g_file_query_info (self->font_file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (info != NULL) {
s = g_content_type_get_description (g_file_info_get_content_type (info));
add_row (grid, _("Type"), s, FALSE);
g_free (s);
g_object_unref (info);
}
if (FT_IS_SFNT (face)) {
gint i, len;
gchar *version = NULL, *copyright = NULL, *description = NULL;
gchar *designer = NULL, *manufacturer = NULL, *license = NULL;
len = FT_Get_Sfnt_Name_Count (face);
for (i = 0; i < len; i++) {
FT_SfntName sname;
if (FT_Get_Sfnt_Name (face, i, &sname) != 0)
continue;
/* only handle the unicode names for US langid */
if (!(sname.platform_id == TT_PLATFORM_MICROSOFT &&
sname.encoding_id == TT_MS_ID_UNICODE_CS &&
sname.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES))
continue;
switch (sname.name_id) {
case TT_NAME_ID_COPYRIGHT:
g_free (copyright);
copyright = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
case TT_NAME_ID_VERSION_STRING:
g_free (version);
version = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
case TT_NAME_ID_DESCRIPTION:
g_free (description);
description = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
case TT_NAME_ID_MANUFACTURER:
g_free (manufacturer);
manufacturer = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
case TT_NAME_ID_DESIGNER:
g_free (designer);
designer = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
case TT_NAME_ID_LICENSE:
g_free (license);
license = g_convert ((gchar *)sname.string, sname.string_len,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
default:
break;
}
}
if (version) {
strip_version (&version);
add_row (grid, _("Version"), version, FALSE);
g_free (version);
}
if (copyright) {
strip_whitespace (&copyright);
add_row (grid, _("Copyright"), copyright, TRUE);
g_free (copyright);
}
if (description) {
strip_whitespace (&description);
add_row (grid, _("Description"), description, TRUE);
g_free (description);
}
if (manufacturer) {
strip_whitespace (&manufacturer);
add_row (grid, _("Manufacturer"), manufacturer, TRUE);
g_free (manufacturer);
}
if (designer) {
strip_whitespace (&designer);
add_row (grid, _("Designer"), designer, TRUE);
g_free (designer);
}
if (license) {
strip_whitespace (&license);
add_row (grid, _("License"), license, TRUE);
g_free (license);
}
} else if (FT_Get_PS_Font_Info (face, &ps_info) == 0) {
gchar *compressed;
if (ps_info.version && g_utf8_validate (ps_info.version, -1, NULL)) {
compressed = g_strcompress (ps_info.version);
strip_version (&compressed);
add_row (grid, _("Version"), compressed, FALSE);
g_free (compressed);
}
if (ps_info.notice && g_utf8_validate (ps_info.notice, -1, NULL)) {
compressed = g_strcompress (ps_info.notice);
strip_whitespace (&compressed);
add_row (grid, _("Copyright"), compressed, TRUE);
g_free (compressed);
}
}
{
char *s = g_strdup_printf ("%ld", face->num_glyphs);
add_row (grid, _("Glyph count"), s, FALSE);
g_free (s);
}
add_row (grid, _("Color glyphs"), FT_HAS_COLOR (face) ? _("yes") : _("no"), FALSE);
{
char *features = get_features (face);
if (features)
add_row (grid, _("Features"), features, TRUE);
g_free (features);
}
if (FT_Get_MM_Var (face, &ft_mm_var) == 0) {
int i;
for (i = 0; i < ft_mm_var->num_axis; i++) {
char *s = describe_axis (&ft_mm_var->axis[i]);
add_row (grid, i == 0 ? _("Axes") : "", s, FALSE);
g_free (s);
}
{
GString *s = g_string_new ("");
for (i = 0; i < ft_mm_var->num_namedstyles; i++) {
describe_instance (face, &ft_mm_var->namedstyle[i], i, s);
}
add_row (grid, _("Instances"), s->str, TRUE);
g_string_free (s, TRUE);
}
}
}
static void
install_button_refresh_appearance (FontViewApplication *self,
GError *error)
{
FT_Face face;
GtkStyleContext *context;
context = gtk_widget_get_style_context (self->install_button);
if (error != NULL) {
gtk_button_set_label (GTK_BUTTON (self->install_button), _("Install Failed"));
gtk_widget_set_sensitive (self->install_button, FALSE);
gtk_style_context_remove_class (context, "suggested-action");
} else {
face = sushi_font_widget_get_ft_face (SUSHI_FONT_WIDGET (self->font_widget));
if (font_view_model_get_iter_for_face (FONT_VIEW_MODEL (self->model), face, NULL)) {
gtk_button_set_label (GTK_BUTTON (self->install_button), _("Installed"));
gtk_widget_set_sensitive (self->install_button, FALSE);
gtk_style_context_remove_class (context, "suggested-action");
} else {
gtk_button_set_label (GTK_BUTTON (self->install_button), _("Install"));
gtk_widget_set_sensitive (self->install_button, TRUE);
gtk_style_context_add_class (context, "suggested-action");
}
}
}
static void
font_install_finished_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
FontViewApplication *self = user_data;
GError *err = NULL;
g_file_copy_finish (G_FILE (source_object), res, &err);
if (err != NULL) {
install_button_refresh_appearance (self, err);
g_debug ("Install failed: %s", err->message);
g_error_free (err);
}
}
static void
font_model_config_changed_cb (FontViewModel *model,
gpointer user_data)
{
FontViewApplication *self = user_data;
if (self->font_file != NULL)
install_button_refresh_appearance (self, NULL);
}
static void
install_button_clicked_cb (GtkButton *button,
gpointer user_data)
{
FontViewApplication *self = user_data;
gchar *dest_filename;
GError *err = NULL;
FcConfig *config;
FcStrList *str_list;
FcChar8 *path;
GFile *xdg_prefix, *home_prefix, *file;
GFile *xdg_location = NULL, *home_location = NULL;
GFile *dest_location = NULL, *dest_file;
config = FcConfigGetCurrent ();
str_list = FcConfigGetFontDirs (config);
home_prefix = g_file_new_for_path (g_get_home_dir ());
xdg_prefix = g_file_new_for_path (g_get_user_data_dir ());
/* pick the XDG location, if any, or fallback to the first location
* under the home directory.
*/
while ((path = FcStrListNext (str_list)) != NULL) {
file = g_file_new_for_path ((const gchar *) path);
if (g_file_has_prefix (file, xdg_prefix)) {
xdg_location = file;
break;
}
if ((home_location == NULL) &&
g_file_has_prefix (file, home_prefix)) {
home_location = file;
break;
}
g_object_unref (file);
}
FcStrListDone (str_list);
g_object_unref (home_prefix);
g_object_unref (xdg_prefix);
if (xdg_location != NULL)
dest_location = g_object_ref (xdg_location);
else if (home_location != NULL)
dest_location = g_object_ref (home_location);
g_clear_object (&home_location);
g_clear_object (&xdg_location);
if (dest_location == NULL) {
g_warning ("Install failed: can't find any configured user font directory.");
return;
}
if (!g_file_query_exists (dest_location, NULL)) {
g_file_make_directory_with_parents (dest_location, NULL, &err);
if (err) {
/* TODO: show error dialog */
g_warning ("Could not create fonts directory: %s", err->message);