summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
authorsu_v <suv-sf@users.sourceforge.net>2013-03-08 08:59:37 +0000
committer~suv <suv-sf@users.sourceforge.net>2013-03-08 08:59:37 +0000
commitd8a3b36b43e0700126a7a25a1d38858d1a1d4836 (patch)
treeeaf5a86ac5542fc33ed8bb697a615e08ddd2ae01 /src/libnrtype
parentchanges_2013_02_25a.patch (diff)
parentDrop deprecated get_vbox method in Gtk::Dialog (diff)
downloadinkscape-d8a3b36b43e0700126a7a25a1d38858d1a1d4836.tar.gz
inkscape-d8a3b36b43e0700126a7a25a1d38858d1a1d4836.zip
merge from trunk (r12181)
(bzr r11668.1.53)
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontFactory.cpp5
-rw-r--r--src/libnrtype/font-lister.cpp793
-rw-r--r--src/libnrtype/font-lister.h197
3 files changed, 961 insertions, 34 deletions
diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp
index a9220d867..a1a9ddc89 100644
--- a/src/libnrtype/FontFactory.cpp
+++ b/src/libnrtype/FontFactory.cpp
@@ -741,6 +741,11 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
continue;
}
+ // Disable synthesized (faux) font faces
+ if (pango_font_face_is_synthesized(faces[currentFace]) ) {
+ continue;
+ }
+
PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
if (faceDescr) {
Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp
index 04859185c..1e3ba01e9 100644
--- a/src/libnrtype/font-lister.cpp
+++ b/src/libnrtype/font-lister.cpp
@@ -10,15 +10,19 @@
#include <glibmm.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/liststore.h>
-
#include "font-lister.h"
#include "FontFactory.h"
+#include "desktop.h"
+#include "desktop-style.h"
+#include "document.h"
+#include "inkscape.h"
+#include "preferences.h"
#include "sp-object.h"
#include "sp-root.h"
-#include "document.h"
#include "xml/repr.h"
-#include "preferences.h"
+
+//#define DEBUG_FONT
namespace Inkscape
{
@@ -29,7 +33,7 @@ namespace Inkscape
FamilyToStylesMap familyStyleMap;
font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap);
-
+
// Grab the family names into a list and then sort them
std::list<Glib::ustring> familyList;
for (FamilyToStylesMap::iterator iter = familyStyleMap.begin();
@@ -47,7 +51,8 @@ namespace Inkscape
if (!familyName.empty()) {
Gtk::TreeModel::iterator treeModelIter = font_list_store->append();
- (*treeModelIter)[FontList.font] = reinterpret_cast<const char*>(g_strdup(familyName.c_str()));
+ //(*treeModelIter)[FontList.family] = reinterpret_cast<const char*>(g_strdup(familyName.c_str()));
+ (*treeModelIter)[FontList.family] = familyName;
// Now go through the styles
GList *styles = NULL;
@@ -62,7 +67,21 @@ namespace Inkscape
(*treeModelIter)[FontList.onSystem] = true;
}
}
+ current_family_row = 0;
+ current_family = "sans-serif";
+ current_style = "Normal";
+ current_fontspec = "sans-serif"; // Empty style -> Normal
+ current_fontspec_system = "Sans";
+
+ /* Create default styles for use when font-family is unknown on system. */
+ default_styles = g_list_append( NULL, g_strdup("Normal") );
+ default_styles = g_list_append( default_styles, g_strdup("Italic") );
+ default_styles = g_list_append( default_styles, g_strdup("Bold") );
+ default_styles = g_list_append( default_styles, g_strdup("Bold Italic") );
+
font_list_store->thaw_notify();
+
+ style_list_store = Gtk::ListStore::create (FontStyleList);
}
// Example of how to use "foreach_iter"
@@ -70,7 +89,7 @@ namespace Inkscape
// FontLister::print_document_font( const Gtk::TreeModel::iterator &iter ) {
// Gtk::TreeModel::Row row = *iter;
// if( !row[FontList.onSystem] ) {
- // std::cout << " Not on system: " << row[FontList.font] << std::endl;
+ // std::cout << " Not on system: " << row[FontList.family] << std::endl;
// return false;
// }
// return true;
@@ -87,28 +106,31 @@ namespace Inkscape
font_list_store->freeze_notify();
+ /* Find if current row is in document or system part of list */
+ gboolean row_is_system = false;
+ if( current_family_row > -1 ) {
+ Gtk::TreePath path;
+ path.push_back( current_family_row );
+ Gtk::TreeModel::iterator iter = font_list_store->get_iter( path );
+ if( iter ) {
+ row_is_system = (*iter)[FontList.onSystem];
+ // std::cout << " In: row: " << current_family_row << " " << (*iter)[FontList.family] << std::endl;
+ }
+ }
+
/* Clear all old document font-family entries */
Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" );
while( iter != font_list_store->children().end() ) {
Gtk::TreeModel::Row row = *iter;
if( !row[FontList.onSystem] ) {
- // std::cout << " Not on system: " << row[FontList.font] << std::endl;
+ // std::cout << " Not on system: " << row[FontList.family] << std::endl;
iter = font_list_store->erase( iter );
} else {
- // std::cout << " First on system: " << row[FontList.font] << std::endl;
+ // std::cout << " First on system: " << row[FontList.family] << std::endl;
break;
}
}
- /* Create default styles for use when font-family is unknown on system. */
- static GList *default_styles = NULL;
- if( default_styles == NULL ) {
- default_styles = g_list_append( default_styles, g_strdup("Normal") );
- default_styles = g_list_append( default_styles, g_strdup("Italic") );
- default_styles = g_list_append( default_styles, g_strdup("Bold") );
- default_styles = g_list_append( default_styles, g_strdup("Bold Italic") );
- }
-
/* Get "font-family"s used in document. */
std::list<Glib::ustring> fontfamilies;
update_font_list_recursive( r, &fontfamilies );
@@ -120,7 +142,7 @@ namespace Inkscape
/* Insert separator */
if( !fontfamilies.empty() ) {
Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend();
- (*treeModelIter)[FontList.font] = "#";
+ (*treeModelIter)[FontList.family] = "#";
(*treeModelIter)[FontList.onSystem] = false;
}
@@ -137,7 +159,7 @@ namespace Inkscape
Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" );
while( iter2 != font_list_store->children().end() ) {
Gtk::TreeModel::Row row = *iter2;
- if( row[FontList.onSystem] && tokens[0].compare( row[FontList.font] ) == 0 ) {
+ if( row[FontList.onSystem] && tokens[0].compare( row[FontList.family] ) == 0 ) {
styles = row[FontList.styles];
break;
}
@@ -146,11 +168,37 @@ namespace Inkscape
}
Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend();
- (*treeModelIter)[FontList.font] = reinterpret_cast<const char*>(g_strdup((*i).c_str()));
+ (*treeModelIter)[FontList.family] = reinterpret_cast<const char*>(g_strdup((*i).c_str()));
(*treeModelIter)[FontList.styles] = styles;
(*treeModelIter)[FontList.onSystem] = false;
}
+ /* Now we do a song and dance to find the correct row as the row corresponding
+ * to the current_family may have changed. We can't simply search for the
+ * family name in the list since it can occur twice, once in the document
+ * font family part and once in the system font family part. Above we determined
+ * which part it is in.
+ */
+ if( current_family_row > -1 ) {
+ int start = 0;
+ if( row_is_system ) start = fontfamilies.size();
+ int length = font_list_store->children().size();
+ for( int i = 0; i < length; ++i ) {
+ int row = i + start;
+ if( row >= length ) row -= length;
+ Gtk::TreePath path;
+ path.push_back( row );
+ Gtk::TreeModel::iterator iter = font_list_store->get_iter( path );
+ if( iter ) {
+ if( current_family.compare( (*iter)[FontList.family] ) == 0 ) {
+ current_family_row = row;
+ break;
+ }
+ }
+ }
+ }
+ // std::cout << " Out: row: " << current_family_row << " " << current_family << std::endl;
+
font_list_store->thaw_notify();
}
@@ -191,7 +239,557 @@ namespace Inkscape
}
}
- Gtk::TreePath
+ Glib::ustring
+ FontLister::canonize_fontspec( Glib::ustring fontspec ) {
+
+ // Pass fontspec to and back from Pango to get a the fontspec in
+ // canonical form. -inkscape-font-specification relies on the
+ // Pango constructed fontspec not changing form. If it does,
+ // this is the place to fix it.
+ PangoFontDescription *descr = pango_font_description_from_string( fontspec.c_str() );
+ gchar* canonized = pango_font_description_to_string ( descr );
+ Glib::ustring Canonized = canonized;
+ g_free( canonized );
+ pango_font_description_free( descr );
+
+ // Pango canonized strings remove space after comma between family names. Put it back.
+ size_t i = 0;
+ while( (i = Canonized.find(",", i)) != std::string::npos) {
+ Canonized.replace(i, 1, ", ");
+ i += 2;
+ }
+
+ return Canonized;
+ }
+
+ Glib::ustring
+ FontLister::system_fontspec( Glib::ustring fontspec ) {
+
+ // Find what Pango thinks is the closest match.
+ Glib::ustring out = fontspec;
+
+ PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str());
+ font_instance *res = (font_factory::Default())->Face(descr);
+ if (res->pFont) {
+ PangoFontDescription *nFaceDesc = pango_font_describe(res->pFont);
+ out = sp_font_description_get_family(nFaceDesc);
+ }
+ pango_font_description_free(descr);
+
+ return out;
+ }
+
+ std::pair<Glib::ustring, Glib::ustring>
+ FontLister::ui_from_fontspec( Glib::ustring fontspec ) {
+
+ PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str());
+ const gchar* family = pango_font_description_get_family(descr);
+ Glib::ustring Family = family;
+
+ // Pango canonized strings remove space after comma between family names. Put it back.
+ size_t i = 0;
+ while( (i = Family.find(",", i)) != std::string::npos) {
+ Family.replace(i, 1, ", ");
+ i += 2;
+ }
+
+ pango_font_description_unset_fields(descr, PANGO_FONT_MASK_FAMILY);
+ gchar* style = pango_font_description_to_string( descr );
+ Glib::ustring Style = style;
+ pango_font_description_free(descr);
+ g_free( style );
+
+ return std::make_pair( Family, Style );
+ }
+
+ std::pair<Glib::ustring, Glib::ustring>
+ FontLister::selection_update () {
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister::selection_update: entrance" << std::endl;
+#endif
+ // Get fontspec from a selection, preferences, or thin air.
+ Glib::ustring fontspec;
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+
+ // Directly from stored font specification.
+ int result =
+ sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
+
+ //std::cout << " Attempting selected style" << std::endl;
+ if( result != QUERY_STYLE_NOTHING && query->text->font_specification.set ) {
+ fontspec = query->text->font_specification.value;
+ //std::cout << " fontspec from query :" << fontspec << ":" << std::endl;
+ }
+
+ // From style
+ if( fontspec.empty() ) {
+ //std::cout << " Attempting desktop style" << std::endl;
+ int rfamily = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
+ int rstyle = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
+
+ // Must have text in selection
+ if( rfamily != QUERY_STYLE_NOTHING && rstyle != QUERY_STYLE_NOTHING ) {
+ fontspec = fontspec_from_style( query );
+ }
+ //std::cout << " fontspec from style :" << fontspec << ":" << std::endl;
+ }
+
+ // From preferences
+ if( fontspec.empty() ) {
+ //std::cout << " Attempting preferences" << std::endl;
+ sp_style_read_from_prefs(query, "/tools/text");
+ fontspec = fontspec_from_style( query );
+ //std::cout << " fontspec from prefs :" << fontspec << ":" << std::endl;
+ }
+ sp_style_unref(query);
+
+ // From thin air
+ if( fontspec.empty() ) {
+ //std::cout << " Attempting thin air" << std::endl;
+ fontspec = current_family + ", " + current_style;
+ //std::cout << " fontspec from thin air :" << fontspec << ":" << std::endl;
+ }
+
+ // Do we really need? Removes spaces between font-families.
+ //current_fontspec = canonize_fontspec( fontspec );
+ current_fontspec = fontspec; // Ignore for now
+
+ current_fontspec_system = system_fontspec( current_fontspec );
+
+ std::pair<Glib::ustring, Glib::ustring> ui = ui_from_fontspec( current_fontspec );
+ set_font_family( ui.first );
+
+#ifdef DEBUG_FONT
+ std::cout << " family_row: :" << current_family_row << ":" << std::endl;
+ std::cout << " canonized: :" << current_fontspec << ":" << std::endl;
+ std::cout << " system: :" << current_fontspec_system << ":" << std::endl;
+ std::cout << " family: :" << current_family << ":" << std::endl;
+ std::cout << " style: :" << current_style << ":" << std::endl;
+ std::cout << "FontLister::selection_update: exit" << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return std::make_pair( current_family, current_style );
+ }
+
+
+ // Set fontspec. If check is false, best style match will not be done.
+ void
+ FontLister::set_fontspec (Glib::ustring new_fontspec, gboolean check) {
+
+ std::pair<Glib::ustring,Glib::ustring> ui = ui_from_fontspec( new_fontspec );
+ Glib::ustring new_family = ui.first;
+ Glib::ustring new_style = ui.second;
+
+#ifdef DEBUG_FONT
+ std::cout << "FontLister::set_fontspec: family: " << new_family
+ << " style:" << new_style << std::endl;
+#endif
+
+ set_font_family( new_family, false );
+ set_font_style( new_style );
+ }
+
+
+ // TODO: use to determine font-selector best style
+ std::pair<Glib::ustring, Glib::ustring>
+ FontLister::new_font_family (Glib::ustring new_family, gboolean check_style ) {
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister::new_font_family: " << new_family << std::endl;
+#endif
+
+ // No need to do anything if new family is same as old family.
+ if( new_family.compare( current_family ) == 0 ) {
+#ifdef DEBUG_FONT
+ std::cout << "FontLister::new_font_family: exit: no change in family." << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return std::make_pair( current_family, current_style );
+ }
+
+ // We need to do two things:
+ // 1. Update style list for new family.
+ // 2. Select best valid style match to old style.
+
+ // For finding style list, use list of first family in font-family list.
+ GList* styles = NULL;
+ Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" );
+ while( iter != font_list_store->children().end() ) {
+
+ Gtk::TreeModel::Row row = *iter;
+
+ if( new_family.compare( row[FontList.family] ) == 0 ) {
+ styles = row[FontList.styles];
+ break;
+ }
+ ++iter;
+ }
+
+ // Newly typed in font-family may not yet be in list... use default list.
+ // TODO: if font-family is list, check if first family in list is on system
+ // and set style accordingly.
+ if( styles == NULL ) {
+ styles = default_styles;
+ }
+
+ // Update style list.
+ style_list_store->freeze_notify();
+ style_list_store->clear();
+
+ for (GList *l=styles; l; l = l->next) {
+ Gtk::TreeModel::iterator treeModelIter = style_list_store->append();
+ (*treeModelIter)[FontStyleList.styles] = (char*)l->data;
+ }
+
+ style_list_store->thaw_notify();
+
+ // Find best match to the style from the old font-family to the
+ // styles available with the new font.
+ // TODO: Maybe check if an exact match exists before using Pango.
+ Glib::ustring best_style = get_best_style_match( new_family, current_style );
+
+#ifdef DEBUG_FONT
+ std::cout << "FontLister::new_font_family: exit: " << new_family << " " << best_style << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return std::make_pair( new_family, best_style );
+ }
+
+ std::pair<Glib::ustring, Glib::ustring>
+ FontLister::set_font_family (Glib::ustring new_family, gboolean check_style) {
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister::set_font_family: " << new_family << std::endl;
+#endif
+
+ std::pair<Glib::ustring, Glib::ustring> ui = new_font_family( new_family, check_style );
+ current_family = ui.first;
+ current_style = ui.second;
+ current_fontspec = canonize_fontspec( current_family + ", " + current_style );
+ current_fontspec_system = system_fontspec( current_fontspec );
+
+#ifdef DEBUG_FONT
+ std::cout << " family_row: :" << current_family_row << ":" << std::endl;
+ std::cout << " canonized: :" << current_fontspec << ":" << std::endl;
+ std::cout << " system: :" << current_fontspec_system << ":" << std::endl;
+ std::cout << " family: :" << current_family << ":" << std::endl;
+ std::cout << " style: :" << current_style << ":" << std::endl;
+ std::cout << "FontLister::set_font_family: end" << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return ui;
+ }
+
+
+ std::pair<Glib::ustring, Glib::ustring>
+ FontLister::set_font_family (int row, gboolean check_style) {
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister::set_font_family( row ): " << row << std::endl;
+#endif
+
+ current_family_row = row;
+ Gtk::TreePath path;
+ path.push_back( row );
+ Glib::ustring new_family = current_family;
+ Gtk::TreeModel::iterator iter = font_list_store->get_iter( path );
+ if( iter ) {
+ new_family = (*iter)[FontList.family];
+ }
+
+ std::pair<Glib::ustring, Glib::ustring> ui = set_font_family( new_family, check_style );
+
+#ifdef DEBUG_FONT
+ std::cout << "FontLister::set_font_family( row ): end" << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return ui;
+ }
+
+
+ void
+ FontLister::set_font_style (Glib::ustring new_style) {
+
+ // TODO: Validate input using Pango. If Pango doesn't recognize a style it will
+ // attach the "invalid" style to the font-family.
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister:set_font_style: " << new_style << std::endl;
+#endif
+
+ current_style = new_style;
+ current_fontspec = canonize_fontspec( current_family + ", " + current_style );
+ current_fontspec_system = system_fontspec( current_fontspec );
+
+#ifdef DEBUG_FONT
+ std::cout << " canonized: :" << current_fontspec << ":" << std::endl;
+ std::cout << " system: :" << current_fontspec_system << ":" << std::endl;
+ std::cout << " family: " << current_family << std::endl;
+ std::cout << " style: " << current_style << std::endl;
+ std::cout << "FontLister::set_font_style: end" << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ }
+
+
+ // We do this ourselves as we can't rely on FontFactory.
+ void
+ FontLister::fill_css( SPCSSAttr *css, Glib::ustring fontspec ) {
+
+ if( fontspec.empty() ) {
+ fontspec = current_fontspec;
+ }
+ std::pair<Glib::ustring,Glib::ustring> ui = ui_from_fontspec( fontspec );
+
+ Glib::ustring family = ui.first;
+
+ sp_repr_css_set_property (css, "-inkscape-font-specification", fontspec.c_str() );
+ sp_repr_css_set_property (css, "font-family", family.c_str() ); //Canonized w/ spaces
+
+ PangoFontDescription *desc = pango_font_description_from_string( fontspec.c_str() );
+ PangoWeight weight = pango_font_description_get_weight( desc );
+ switch ( weight ) {
+ case PANGO_WEIGHT_THIN:
+ sp_repr_css_set_property (css, "font-weight", "100" );
+ break;
+ case PANGO_WEIGHT_ULTRALIGHT:
+ sp_repr_css_set_property (css, "font-weight", "200" );
+ break;
+ case PANGO_WEIGHT_LIGHT:
+ sp_repr_css_set_property (css, "font-weight", "300" );
+ break;
+ case PANGO_WEIGHT_BOOK:
+ sp_repr_css_set_property (css, "font-weight", "380" );
+ break;
+ case PANGO_WEIGHT_NORMAL:
+ sp_repr_css_set_property (css, "font-weight", "normal" );
+ break;
+ case PANGO_WEIGHT_MEDIUM:
+ sp_repr_css_set_property (css, "font-weight", "500" );
+ break;
+ case PANGO_WEIGHT_SEMIBOLD:
+ sp_repr_css_set_property (css, "font-weight", "600" );
+ break;
+ case PANGO_WEIGHT_BOLD:
+ sp_repr_css_set_property (css, "font-weight", "bold" );
+ break;
+ case PANGO_WEIGHT_ULTRABOLD:
+ sp_repr_css_set_property (css, "font-weight", "800" );
+ break;
+ case PANGO_WEIGHT_HEAVY:
+ sp_repr_css_set_property (css, "font-weight", "900" );
+ break;
+ case PANGO_WEIGHT_ULTRAHEAVY:
+ sp_repr_css_set_property (css, "font-weight", "1000" );
+ break;
+ }
+
+ PangoStyle style = pango_font_description_get_style( desc );
+ switch ( style ) {
+ case PANGO_STYLE_NORMAL:
+ sp_repr_css_set_property (css, "font-style", "normal" );
+ break;
+ case PANGO_STYLE_OBLIQUE:
+ sp_repr_css_set_property (css, "font-style", "oblique" );
+ break;
+ case PANGO_STYLE_ITALIC:
+ sp_repr_css_set_property (css, "font-style", "italic" );
+ break;
+ }
+
+ PangoStretch stretch = pango_font_description_get_stretch( desc );
+ switch ( stretch ) {
+ case PANGO_STRETCH_ULTRA_CONDENSED:
+ sp_repr_css_set_property (css, "font-stretch", "ultra-condensed" );
+ break;
+ case PANGO_STRETCH_EXTRA_CONDENSED:
+ sp_repr_css_set_property (css, "font-stretch", "extra-condensed" );
+ break;
+ case PANGO_STRETCH_CONDENSED:
+ sp_repr_css_set_property (css, "font-stretch", "condensed" );
+ break;
+ case PANGO_STRETCH_SEMI_CONDENSED:
+ sp_repr_css_set_property (css, "font-stretch", "semi-condensed" );
+ break;
+ case PANGO_STRETCH_NORMAL:
+ sp_repr_css_set_property (css, "font-stretch", "normal" );
+ break;
+ case PANGO_STRETCH_SEMI_EXPANDED:
+ sp_repr_css_set_property (css, "font-stretch", "semi-expanded" );
+ break;
+ case PANGO_STRETCH_EXPANDED:
+ sp_repr_css_set_property (css, "font-stretch", "expanded" );
+ break;
+ case PANGO_STRETCH_EXTRA_EXPANDED:
+ sp_repr_css_set_property (css, "font-stretch", "extra-expanded" );
+ break;
+ case PANGO_STRETCH_ULTRA_EXPANDED:
+ sp_repr_css_set_property (css, "font-stretch", "ultra-expanded" );
+ break;
+ }
+
+ PangoVariant variant = pango_font_description_get_variant( desc );
+ switch ( variant ) {
+ case PANGO_VARIANT_NORMAL:
+ sp_repr_css_set_property (css, "font-variant", "normal" );
+ break;
+ case PANGO_VARIANT_SMALL_CAPS:
+ sp_repr_css_set_property (css, "font-variant", "small-caps" );
+ break;
+ }
+ }
+
+ // We do this ourselves as we can't rely on FontFactory.
+ Glib::ustring
+ FontLister::fontspec_from_style (SPStyle* style) {
+
+ //std::cout << "FontLister:fontspec_from_style: " << std::endl;
+
+ Glib::ustring fontspec;
+ if (style) {
+
+ // First try to use the font specification if it is set
+ if (style->text->font_specification.set
+ && style->text->font_specification.value
+ && *style->text->font_specification.value) {
+
+ fontspec = style->text->font_specification.value;
+
+ } else {
+
+ fontspec = style->text->font_family.value;
+ fontspec += ",";
+
+ switch (style->font_weight.computed) {
+
+ case SP_CSS_FONT_WEIGHT_100:
+ fontspec += " 100";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_200:
+ fontspec += " 200";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_300:
+ fontspec += " 300";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_400:
+ case SP_CSS_FONT_WEIGHT_NORMAL:
+ //fontspec += " normal";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_500:
+ fontspec += " 500";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_600:
+ fontspec += " 600";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_700:
+ case SP_CSS_FONT_WEIGHT_BOLD:
+ fontspec += " bold";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_800:
+ fontspec += " 800";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_900:
+ fontspec += " 900";
+ break;
+
+ case SP_CSS_FONT_WEIGHT_LIGHTER:
+ case SP_CSS_FONT_WEIGHT_BOLDER:
+ default:
+ g_warning("Unrecognized font_weight.computed value");
+ break;
+ }
+
+ switch (style->font_style.computed) {
+ case SP_CSS_FONT_STYLE_ITALIC:
+ fontspec += " italic";
+ break;
+
+ case SP_CSS_FONT_STYLE_OBLIQUE:
+ fontspec += " oblique";
+ break;
+
+ case SP_CSS_FONT_STYLE_NORMAL:
+ default:
+ //fontspec += " normal";
+ break;
+ }
+
+ switch (style->font_stretch.computed) {
+
+ case SP_CSS_FONT_STRETCH_ULTRA_CONDENSED:
+ fontspec += " extra_condensed";
+ break;
+
+ case SP_CSS_FONT_STRETCH_EXTRA_CONDENSED:
+ fontspec += " extra_condensed";
+ break;
+
+ case SP_CSS_FONT_STRETCH_CONDENSED:
+ case SP_CSS_FONT_STRETCH_NARROWER:
+ fontspec += " condensed";
+ break;
+
+ case SP_CSS_FONT_STRETCH_SEMI_CONDENSED:
+ fontspec += " semi_condensed";
+ break;
+
+ case SP_CSS_FONT_STRETCH_NORMAL:
+ //fontspec += " normal";
+ break;
+
+ case SP_CSS_FONT_STRETCH_SEMI_EXPANDED:
+ fontspec += " semi_expanded";
+ break;
+
+ case SP_CSS_FONT_STRETCH_EXPANDED:
+ case SP_CSS_FONT_STRETCH_WIDER:
+ fontspec += " expanded";
+ break;
+
+ case SP_CSS_FONT_STRETCH_EXTRA_EXPANDED:
+ fontspec += " extra_expanded";
+ break;
+
+ case SP_CSS_FONT_STRETCH_ULTRA_EXPANDED:
+ fontspec += " ultra_expanded";
+ break;
+
+ default:
+ //fontspec += " normal";
+ break;
+ }
+
+ switch (style->font_variant.computed) {
+
+ case SP_CSS_FONT_VARIANT_SMALL_CAPS:
+ fontspec += "small-caps";
+ break;
+
+ default:
+ //fontspec += "normal";
+ break;
+ }
+ }
+ }
+ return canonize_fontspec( fontspec );
+ }
+
+
+ Gtk::TreeModel::Row
FontLister::get_row_for_font (Glib::ustring family)
{
Gtk::TreePath path;
@@ -201,8 +799,8 @@ namespace Inkscape
Gtk::TreeModel::Row row = *iter;
- if( family.compare( row[FontList.font] ) == 0 ) {
- return font_list_store->get_path( iter );
+ if( family.compare( row[FontList.family] ) == 0 ) {
+ return row;
}
++iter;
@@ -211,6 +809,149 @@ namespace Inkscape
throw FAMILY_NOT_FOUND;
}
+ Gtk::TreePath
+ FontLister::get_path_for_font (Glib::ustring family)
+ {
+ return font_list_store->get_path( get_row_for_font ( family ) );
+ }
+
+ Gtk::TreeModel::Row
+ FontLister::get_row_for_style (Glib::ustring style)
+ {
+ Gtk::TreePath path;
+
+ Gtk::TreeModel::iterator iter = style_list_store->get_iter( "0" );
+ while( iter != style_list_store->children().end() ) {
+
+ Gtk::TreeModel::Row row = *iter;
+
+ if( style.compare( row[FontStyleList.styles] ) == 0 ) {
+ return row;
+ }
+
+ ++iter;
+ }
+
+ throw STYLE_NOT_FOUND;
+ }
+
+ static gint
+ compute_distance (const PangoFontDescription *a,
+ const PangoFontDescription *b ) {
+
+ // Weight: multiples of 100
+ gint distance = abs( pango_font_description_get_weight( a ) -
+ pango_font_description_get_weight( b ) );
+
+ distance += 10000 * abs( pango_font_description_get_stretch( a ) -
+ pango_font_description_get_stretch( b ) );
+
+ PangoStyle style_a = pango_font_description_get_style( a );
+ PangoStyle style_b = pango_font_description_get_style( b );
+ if( style_a != style_b ) {
+ if( (style_a == PANGO_STYLE_OBLIQUE && style_b == PANGO_STYLE_ITALIC) ||
+ (style_b == PANGO_STYLE_OBLIQUE && style_a == PANGO_STYLE_ITALIC) ) {
+ distance += 1000; // Oblique and italic are almost the same
+ } else {
+ distance += 100000; // Normal vs oblique/italic, not so similar
+ }
+ }
+
+ // Normal vs small-caps
+ distance += 1000000 * abs( pango_font_description_get_variant( a ) -
+ pango_font_description_get_variant( b ) );
+ return distance;
+ }
+
+ // This is inspired by pango_font_description_better_match, but that routine
+ // always returns false if variant or stretch are different. This means, for
+ // example, that PT Sans Narrow with style Bold Condensed is never matched
+ // to another font-family with Bold style.
+ gboolean
+ font_description_better_match( PangoFontDescription* target,
+ PangoFontDescription* old_desc,
+ PangoFontDescription* new_desc ) {
+
+ if( old_desc == NULL ) return true;
+ if( new_desc == NULL ) return false;
+
+ int old_distance = compute_distance( target, old_desc );
+ int new_distance = compute_distance( target, new_desc );
+ //std::cout << "font_description_better_match: old: " << old_distance << std::endl;
+ //std::cout << " new: " << new_distance << std::endl;
+
+ return (new_distance < old_distance );
+ }
+
+ // void
+ // font_description_dump( PangoFontDescription* target ) {
+ // std::cout << " Font: " << pango_font_description_to_string( target ) << std::endl;
+ // std::cout << " style: " << pango_font_description_get_style( target ) << std::endl;
+ // std::cout << " weight: " << pango_font_description_get_weight( target ) << std::endl;
+ // std::cout << " variant: " << pango_font_description_get_variant( target ) << std::endl;
+ // std::cout << " stretch: " << pango_font_description_get_stretch( target ) << std::endl;
+ // std::cout << " gravity: " << pango_font_description_get_gravity( target ) << std::endl;
+ // }
+
+ /* Returns style string */
+ // TODO: Remove or turn into function to be used by new_font_family.
+ Glib::ustring
+ FontLister::get_best_style_match (Glib::ustring family, Glib::ustring target_style) {
+
+#ifdef DEBUG_FONT
+ std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
+ std::cout << "FontLister::get_best_style_match: " << family << " : " << target_style << std::endl;
+#endif
+
+ Glib::ustring fontspec = family + ", " + target_style;
+
+ Gtk::TreeModel::Row row;
+ try {
+ row = get_row_for_font( family );
+ } catch (...) {
+ //std::cout << " ERROR: can't find family: " << family << std::endl;
+ return (target_style);
+ }
+
+ PangoFontDescription* target = pango_font_description_from_string( fontspec.c_str() );
+ PangoFontDescription* best = NULL;
+
+ //font_description_dump( target );
+
+ GList* styles = row[FontList.styles];
+ for (GList *l=styles; l; l = l->next) {
+ Glib::ustring fontspec = family + ", " + (char*)l->data;
+ PangoFontDescription* candidate = pango_font_description_from_string( fontspec.c_str() );
+ //font_description_dump( candidate );
+ //std::cout << " " << font_description_better_match( target, best, candidate ) << std::endl;
+ if( font_description_better_match( target, best, candidate ) ) {
+ pango_font_description_free( best );
+ best = candidate;
+ //std::cout << " ... better: " << std::endl;
+ } else {
+ pango_font_description_free( candidate );
+ //std::cout << " ... not better: " << std::endl;
+ }
+ }
+
+ Glib::ustring best_style = target_style;
+ if( best ) {
+ pango_font_description_unset_fields( best, PANGO_FONT_MASK_FAMILY );
+ best_style = pango_font_description_to_string( best );
+ }
+
+ if( target ) pango_font_description_free( target );
+ if( best ) pango_font_description_free( best );
+
+
+#ifdef DEBUG_FONT
+ std::cout << " Returning: " << best_style << std::endl;
+ std::cout << "FontLister::get_best_style_match: exit" << std::endl;
+ std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
+#endif
+ return best_style;
+ }
+
FontLister::~FontLister ()
{
};
@@ -220,6 +961,12 @@ namespace Inkscape
{
return font_list_store;
}
+
+ const Glib::RefPtr<Gtk::ListStore>
+ FontLister::get_style_list () const
+ {
+ return style_list_store;
+ }
}
// Helper functions
diff --git a/src/libnrtype/font-lister.h b/src/libnrtype/font-lister.h
index 751350407..5c48bf7a8 100644
--- a/src/libnrtype/font-lister.h
+++ b/src/libnrtype/font-lister.h
@@ -25,12 +25,35 @@
class SPObject;
class SPDocument;
+class SPCSSAttr;
+struct SPStyle;
namespace Inkscape
{
/**
* This class enumerates fonts using libnrtype into reusable data stores and
- * allows for random access to the font list
+ * allows for random access to the font-family list and the font-style list.
+ * Setting the font-family updates the font-style list. "Style" in this case
+ * refers to everything but family and size (e.g. italic/oblique, weight).
+ *
+ * This class handles font-family lists and fonts that are not on the system,
+ * where there is not an entry in the fontInstanceMap.
+ *
+ * This class uses the idea of "font_spec". This is a plain text string as used by
+ * Pango. It is similar to the CSS font shorthand except that font-family comes
+ * first and in this class the font-size is not used.
+ *
+ * This class uses the FontFactory class to get a list of system fonts
+ * and to find best matches via Pango. The Pango interface is only setup
+ * to deal with fonts that are on the system so care must be taken. For
+ * example, best matches should only be done with the first font-family
+ * in a font-family list. If the first font-family is not on the system
+ * then a generic font-family should be used (sans-serif -> Sans).
+ *
+ * This class is used by the UI interface (text-toolbar, font-select, etc.).
+ *
+ * "Font" includes family and style. It should not be used when one
+ * means font-family.
*/
class FontLister
{
@@ -38,13 +61,14 @@ namespace Inkscape
enum Exceptions
{
- FAMILY_NOT_FOUND
+ FAMILY_NOT_FOUND,
+ STYLE_NOT_FOUND
};
virtual ~FontLister ();
- /** GtkTreeModelColumnRecord for the font list Gtk::ListStore
+ /** GtkTreeModelColumnRecord for the font-family list Gtk::ListStore
*/
class FontListClass
: public Gtk::TreeModelColumnRecord
@@ -52,10 +76,9 @@ namespace Inkscape
public:
/** Column containing the family name
*/
- Gtk::TreeModelColumn<Glib::ustring> font;
+ Gtk::TreeModelColumn<Glib::ustring> family;
- /** Column containing an std::vector<std::string> with style names
- * for the corresponding family
+ /** Column containing the styles for each family name.
*/
Gtk::TreeModelColumn<GList*> styles;
@@ -65,7 +88,7 @@ namespace Inkscape
FontListClass ()
{
- add (font);
+ add (family);
add (styles);
add (onSystem);
}
@@ -73,7 +96,23 @@ namespace Inkscape
FontListClass FontList;
- /** Returns the ListStore with the font names
+ class FontStyleListClass
+ : public Gtk::TreeModelColumnRecord
+ {
+ public:
+ /** Column containing the styles
+ */
+ Gtk::TreeModelColumn<Glib::ustring> styles;
+
+ FontStyleListClass ()
+ {
+ add (styles);
+ }
+ };
+
+ FontStyleListClass FontStyleList;
+
+ /** Returns the ListStore with the family names
*
* The return is const and the function is declared as const.
* The ListStore is ready to be used after class instantiation
@@ -82,6 +121,12 @@ namespace Inkscape
const Glib::RefPtr<Gtk::ListStore>
get_font_list () const;
+ /** Returns the ListStore with the styles
+ *
+ */
+ const Glib::RefPtr<Gtk::ListStore>
+ get_style_list () const;
+
/** Updates font list to include fonts in document
*
*/
@@ -96,13 +141,126 @@ namespace Inkscape
static Inkscape::FontLister*
get_instance ()
{
- static Inkscape::FontLister* instance = new Inkscape::FontLister();
+ static Inkscape::FontLister* instance = new Inkscape::FontLister();
return instance;
}
- Gtk::TreePath
+ /** Takes a hand written font spec and returns a Pango generated one in
+ * standard form.
+ */
+ Glib::ustring canonize_fontspec( Glib::ustring fontspec );
+
+ /** Find closest system font to given font.
+ */
+ Glib::ustring system_fontspec( Glib::ustring fontspec );
+
+ /** Gets font-family and style from fontspec.
+ * font-family and style returned.
+ */
+ std::pair<Glib::ustring, Glib::ustring>
+ ui_from_fontspec (Glib::ustring fontspec);
+
+ /** Sets font-family and style after a selection change.
+ * New font-family and style returned.
+ */
+ std::pair<Glib::ustring, Glib::ustring>
+ selection_update ();
+
+ /** Sets current_fontspec, etc. If check is false, won't
+ * try to find best style match (assumes style in fontspec
+ * valid for given font-family).
+ */
+ void
+ set_fontspec (Glib::ustring fontspec, gboolean check=true);
+
+ Glib::ustring
+ get_fontspec ()
+ {
+ return current_fontspec;
+ }
+
+ /** Changes font-family, updating style list and attempting to find
+ * closest style to current_style style (if check_style is true).
+ * New font-family and style returned.
+ * Does NOT update current_family and current_style.
+ * (For potential use in font-selector which doesn't update until
+ * "Apply" button clicked.)
+ */
+ std::pair<Glib::ustring, Glib::ustring>
+ new_font_family (Glib::ustring family, gboolean check_style = true);
+
+ /** Sets font-family, updating style list and attempting
+ * to find closest style to old current_style.
+ * New font-family and style returned.
+ * Updates current_family and current_style.
+ * Calls new_font_family().
+ * (For use in text-toolbar where update is immediate.)
+ */
+ std::pair<Glib::ustring, Glib::ustring>
+ set_font_family (Glib::ustring family, gboolean check_style = true);
+
+ /** Sets font-family from row in list store.
+ * The row can be used to determine if we are in the
+ * document or system part of the font-family list.
+ * This is needed to handle scrolling through the
+ * font-family list correctly.
+ * Calls set_font_family().
+ */
+ std::pair<Glib::ustring, Glib::ustring>
+ set_font_family (int row, gboolean check_style = true);
+
+ Glib::ustring
+ get_font_family ()
+ {
+ return current_family;
+ }
+
+ int
+ get_font_family_row ()
+ {
+ return current_family_row;
+ }
+
+ /** Sets style. Does not validate style for family.
+ */
+ void
+ set_font_style (Glib::ustring style);
+
+ Glib::ustring
+ get_font_style ()
+ {
+ return current_style;
+ }
+
+ Glib::ustring
+ fontspec_from_style (SPStyle* style);
+
+ /** Fill css using current_fontspec.
+ */
+ void
+ fill_css( SPCSSAttr *css, Glib::ustring fontspec = "" );
+
+ Gtk::TreeModel::Row
get_row_for_font (Glib::ustring family);
+ Gtk::TreePath
+ get_path_for_font (Glib::ustring family);
+
+ Gtk::TreeModel::Row
+ get_row_for_style (Glib::ustring style);
+
+ Gtk::TreePath
+ get_path_for_style (Glib::ustring style);
+
+ std::pair<Gtk::TreePath, Gtk::TreePath>
+ get_paths (Glib::ustring family, Glib::ustring style);
+
+ /** Return best style match for new font given style for old font.
+ */
+ Glib::ustring
+ get_best_style_match (Glib::ustring family, Glib::ustring style);
+
+ /* Not Used */
const NRNameList
get_name_list () const
{
@@ -116,7 +274,24 @@ namespace Inkscape
NRNameList families;
Glib::RefPtr<Gtk::ListStore> font_list_store;
+ Glib::RefPtr<Gtk::ListStore> style_list_store;
+
+ /** Info for currently selected font (what is shown in the UI).
+ * May include font-family lists and fonts not on system.
+ */
+ int current_family_row;
+ Glib::ustring current_family;
+ Glib::ustring current_style;
+ Glib::ustring current_fontspec;
+
+ /** fontspec of system font closest to current_fontspec.
+ * (What the system will use to display current_fontspec.)
+ */
+ Glib::ustring current_fontspec_system;
+ /** If a font-family is not on system, this list of styles is used.
+ */
+ GList *default_styles;
};
}
@@ -125,7 +300,7 @@ namespace Inkscape
static gboolean font_lister_separator_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/)
{
gchar* text = 0;
- gtk_tree_model_get(model, iter, 0, &text, -1 ); // Column 0: FontList.font
+ gtk_tree_model_get(model, iter, 0, &text, -1 ); // Column 0: FontList.family
return (text && strcmp(text,"#") == 0);
}