diff options
| author | Markus Engel <markus.engel@tum.de> | 2013-03-29 23:52:42 +0000 |
|---|---|---|
| committer | Markus Engel <markus.engel@tum.de> | 2013-03-29 23:52:42 +0000 |
| commit | a168040d5a452544328a1e6ad35aaac351f94d44 (patch) | |
| tree | fae1ba829f543a473da281bd5fa6e4deabbf6912 /src/libnrtype | |
| parent | Removed function pointers from SPObject and subclasses. (diff) | |
| parent | Dutch translation update (diff) | |
| download | inkscape-a168040d5a452544328a1e6ad35aaac351f94d44.tar.gz inkscape-a168040d5a452544328a1e6ad35aaac351f94d44.zip | |
merged from trunk
(bzr r11608.1.56)
Diffstat (limited to 'src/libnrtype')
| -rw-r--r-- | src/libnrtype/FontFactory.cpp | 105 | ||||
| -rw-r--r-- | src/libnrtype/FontFactory.h | 3 | ||||
| -rw-r--r-- | src/libnrtype/FontInstance.cpp | 18 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Input.cpp | 6 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Output.cpp | 11 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG.h | 4 | ||||
| -rw-r--r-- | src/libnrtype/Makefile_insert | 1 | ||||
| -rw-r--r-- | src/libnrtype/font-lister.cpp | 1019 | ||||
| -rw-r--r-- | src/libnrtype/font-lister.h | 243 | ||||
| -rw-r--r-- | src/libnrtype/font-style-to-pos.h | 2 |
10 files changed, 1318 insertions, 94 deletions
diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index 8de9d4795..74c706a1b 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -14,28 +14,19 @@ # include "config.h" #endif -#include <glibmm.h> -#include <glib.h> -#include <glibmm/i18n.h> // _() +#include <glibmm/i18n.h> #include <pango/pangoft2.h> #include "libnrtype/FontFactory.h" #include "libnrtype/font-instance.h" #include "util/unordered-containers.h" -#if !PANGO_VERSION_CHECK(1,24,0) -#define PANGO_WEIGHT_THIN static_cast<PangoWeight>(100) -#define PANGO_WEIGHT_BOOK static_cast<PangoWeight>(380) -#define PANGO_WEIGHT_MEDIUM static_cast<PangoWeight>(500) -#define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000) -#endif - typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; // need to avoid using the size field size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { int h = 0; h *= 1128467; - char const *theF = pango_font_description_get_family(x); + char const *theF = sp_font_description_get_family(x); h += (theF)?g_str_hash(theF):0; h *= 1128467; h += (int)pango_font_description_get_style(x); @@ -49,8 +40,8 @@ size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { } bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const { //if ( pango_font_description_equal(a,b) ) return true; - char const *fa = pango_font_description_get_family(a); - char const *fb = pango_font_description_get_family(b); + char const *fa = sp_font_description_get_family(a); + char const *fb = sp_font_description_get_family(b); if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false; if ( fa && fb && strcmp(fa,fb) != 0 ) return false; if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false; @@ -276,7 +267,7 @@ family_name_compare(char const *a, char const *b) #endif } -void noop(...) {} +static void noop(...) {} //#define PANGO_DEBUG g_print #define PANGO_DEBUG noop @@ -285,7 +276,7 @@ void noop(...) {} ///////////////////// FontFactory #ifndef USE_PANGO_WIN32 // the substitute function to tell fontconfig to enforce outline fonts -void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/) +static void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/) { FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue); //char *fam = NULL; @@ -322,9 +313,15 @@ font_factory::font_factory(void) : { #ifdef USE_PANGO_WIN32 #else - pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72); - fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer); - pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL); + pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fontServer), + 72, 72); + + fontContext = pango_font_map_create_context(fontServer); + + pango_ft2_font_map_set_default_substitute(PANGO_FT2_FONT_MAP(fontServer), + FactorySubstituteFunc, + this, + NULL); #endif } @@ -396,6 +393,33 @@ Glib::ustring font_factory::ConstructFontSpecification(font_instance *font) return pangoString; } +/* + * Wrap calls to pango_font_description_get_family + * and replace some of the pango font names with generic css names + * http://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#generic-font-families + * + * This function should be called in place of pango_font_description_get_family() + */ +const char *sp_font_description_get_family(PangoFontDescription const *fontDescr) { + + static std::map<Glib::ustring, Glib::ustring> fontNameMap; + std::map<Glib::ustring, Glib::ustring>::iterator it; + + if (fontNameMap.empty()) { + fontNameMap.insert(std::make_pair("Sans", "sans-serif")); + fontNameMap.insert(std::make_pair("Serif", "serif")); + fontNameMap.insert(std::make_pair("Monospace", "monospace")); + } + + const char *pangoFamily = pango_font_description_get_family(fontDescr); + + if (pangoFamily && ((it = fontNameMap.find(pangoFamily)) != fontNameMap.end())) { + return ((Glib::ustring)it->second).c_str(); + } + + return pangoFamily; +} + Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr) { Glib::ustring family; @@ -404,7 +428,8 @@ Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDe if (fontDescr) { // For now, keep it as family name taken from pango - const char *pangoFamily = pango_font_description_get_family(fontDescr); + const char *pangoFamily = sp_font_description_get_family(fontDescr); + if( pangoFamily ) { family = pangoFamily; } @@ -455,7 +480,7 @@ Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & // what constitutes a "family" in our own UI may be different from how Pango // sees it. - // Find the PangoFontDescription associated with the font specification string. + // Find the PangoFontDescription associated with the old font specification string. PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec); @@ -465,15 +490,23 @@ Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & // Make copy PangoFontDescription *descr = pango_font_description_copy((*it).second); - // Grab the UI Family string from the descr + // Grab the old UI Family string from the descr Glib::ustring uiFamily = GetUIFamilyString(descr); // Replace the UI Family name with the new family name std::size_t found = fontSpec.find(uiFamily); if (found != Glib::ustring::npos) { + + // Add comma to end of newFamily... commas at end don't hurt but are + // required if the last part of a family name is a valid font style + // (e.g. "Arial Black"). + Glib::ustring newFamilyComma = newFamily; + if( *newFamilyComma.rbegin() != ',' ) { + newFamilyComma += ","; + } newFontSpec = fontSpec; newFontSpec.erase(found, uiFamily.size()); - newFontSpec.insert(found, newFamily); + newFontSpec.insert(found, newFamilyComma); // If the new font specification does not exist in the reference maps, // search for the next best match for the faces in that style @@ -711,12 +744,26 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map) Glib::ustring familyUIName = GetUIFamilyString(faceDescr); Glib::ustring styleUIName = GetUIStyleString(faceDescr); + // Disable synthesized (faux) font faces except for CSS generic faces + if (pango_font_face_is_synthesized(faces[currentFace]) ) { + if( familyUIName.compare( "sans-serif" ) != 0 && + familyUIName.compare( "serif" ) != 0 && + familyUIName.compare( "monospace" ) != 0 && + familyUIName.compare( "fantasy" ) != 0 && + familyUIName.compare( "cursive" ) != 0 ) { + //std::cout << "faux: " << familyUIName << " | " << styleUIName << std::endl; + continue; + } + } + if (!familyUIName.empty() && !styleUIName.empty()) { + // Find the right place to put the style information, adding // a map entry for the family name if it doesn't yet exist FamilyToStylesMap::iterator iter = map->find(familyUIName); + // Insert new family if (iter == map->end()) { map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>())); } @@ -746,6 +793,7 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map) ConstructFontSpecification(faceDescr))); fontInstanceMap.insert( std::make_pair(ConstructFontSpecification(faceDescr), faceDescr)); + } else { pango_font_description_free(faceDescr); } @@ -816,7 +864,14 @@ font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const g_assert(uiFamily && uiStyle); if (uiFamily && uiStyle) { - Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle); + + // If font list, take only first font in list + gchar** tokens = g_strsplit( uiFamily, ",", 0 ); + g_strstrip( tokens[0] ); + + Glib::ustring uiString = Glib::ustring(tokens[0]) + Glib::ustring(uiStyle); + + g_strfreev( tokens ); UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString); @@ -855,7 +910,7 @@ font_instance* font_factory::FaceFromPangoString(char const *pangoString) descr = pango_font_description_from_string(pangoString); } - if (descr && (pango_font_description_get_family(descr) != NULL)) { + if (descr && (sp_font_description_get_family(descr) != NULL)) { fontInstance = Face(descr); } @@ -903,7 +958,7 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) // workaround for bug #1025565. // fonts without families blow up Pango. - if (pango_font_description_get_family(descr) != NULL) { + if (sp_font_description_get_family(descr) != NULL) { nFace = pango_font_map_load_font(fontServer,fontContext,descr); } else { diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 42f975ab7..12046079e 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -54,6 +54,9 @@ struct font_descr_equal : public std::binary_function<PangoFontDescription*, Pan int style_name_compare(char const *aa, char const *bb); int family_name_compare(char const *a, char const *b); +// Wraps calls to pango_font_description_get_family with some name substitution +const char *sp_font_description_get_family(PangoFontDescription const *fontDescr); + // Map type for gathering UI family and style strings typedef std::map<Glib::ustring, std::list<Glib::ustring> > FamilyToStylesMap; diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index f8b2c3b9d..61225ad0c 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -27,13 +27,6 @@ #include "livarot/Path.h" #include "util/unordered-containers.h" -#if !PANGO_VERSION_CHECK(1,24,0) -#define PANGO_WEIGHT_THIN static_cast<PangoWeight>(100) -#define PANGO_WEIGHT_BOOK static_cast<PangoWeight>(380) -#define PANGO_WEIGHT_MEDIUM static_cast<PangoWeight>(500) -#define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000) -#endif - struct font_style_hash : public std::unary_function<font_style, size_t> { size_t operator()(font_style const &x) const; @@ -292,14 +285,14 @@ unsigned int font_instance::Attribute(const gchar *key, gchar *str, unsigned int bool b = (weight >= PANGO_WEIGHT_BOLD); res = g_strdup_printf ("%s%s%s%s", - pango_font_description_get_family(descr), + sp_font_description_get_family(descr), (b || i || o) ? "-" : "", (b) ? "Bold" : "", (i) ? "Italic" : ((o) ? "Oblique" : "") ); free_res = true; } } else if ( strcmp(key,"family") == 0 ) { - res=(char*)pango_font_description_get_family(descr); + res=(char*)sp_font_description_get_family(descr); free_res=false; } else if ( strcmp(key,"style") == 0 ) { PangoStyle v=pango_font_description_get_style(descr); @@ -406,7 +399,7 @@ void font_instance::InitTheFace() SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE); SelectObject(daddy->hScreenDC,theFace); #else - theFace=pango_ft2_font_get_face(pFont); // Deprecated, use pango_fc_font_lock_face() instead + theFace=pango_fc_font_lock_face(PANGO_FC_FONT(pFont)); if ( theFace ) { FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol); } @@ -418,6 +411,8 @@ void font_instance::FreeTheFace() #ifdef USE_PANGO_WIN32 SelectObject(daddy->hScreenDC,GetStockObject(SYSTEM_FONT)); pango_win32_font_cache_unload(daddy->pangoFontCache,theFace); +#else + pango_fc_font_unlock_face(PANGO_FC_FONT(pFont)); #endif theFace=NULL; } @@ -461,12 +456,13 @@ int font_instance::MapUnicodeChar(gunichar c) #ifdef USE_PANGO_WIN32 res = pango_win32_font_get_glyph_index(pFont, c); #else - theFace = pango_ft2_font_get_face(pFont); + theFace = pango_fc_font_lock_face(PANGO_FC_FONT(pFont)); if ( c > 0xf0000 ) { res = CLAMP(c, 0xf0000, 0x1fffff) - 0xf0000; } else { res = FT_Get_Char_Index(theFace, c); } + pango_fc_font_unlock_face(PANGO_FC_FONT(pFont)); #endif } return res; diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 07bf207c8..10310b4aa 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -19,12 +19,6 @@ #include "sp-string.h" #include "FontFactory.h" -#if !PANGO_VERSION_CHECK(1,24,0) -#define PANGO_WEIGHT_THIN static_cast<PangoWeight>(100) -#define PANGO_WEIGHT_BOOK static_cast<PangoWeight>(380) -#define PANGO_WEIGHT_MEDIUM static_cast<PangoWeight>(500) -#define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000) -#endif namespace Inkscape { namespace Text { diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index 4bdd2c6cb..0f853c681 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -21,18 +21,11 @@ #include "display/curve.h" #include <2geom/pathvector.h> -#if !PANGO_VERSION_CHECK(1,24,0) -#define PANGO_WEIGHT_THIN static_cast<PangoWeight>(100) -#define PANGO_WEIGHT_BOOK static_cast<PangoWeight>(380) -#define PANGO_WEIGHT_MEDIUM static_cast<PangoWeight>(500) -#define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000) -#endif namespace Inkscape { namespace Extension { namespace Internal { class CairoRenderContext; - class CairoGlyphInfo; } } } @@ -346,7 +339,7 @@ Glib::ustring Layout::getFontFamily(unsigned span_index) const return ""; if (_spans[span_index].font) { - return pango_font_description_get_family(_spans[span_index].font->descr); + return sp_font_description_get_family(_spans[span_index].font->descr); } return ""; @@ -374,7 +367,7 @@ Glib::ustring Layout::dumpAsText() const snprintf(line, sizeof(line), " in chunk %d (x=%f, baselineshift=%f)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift); result += line; if (_spans[span_index].font) { - snprintf(line, sizeof(line), " font '%s' %f %s %s\n", pango_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr))); + snprintf(line, sizeof(line), " font '%s' %f %s %s\n", sp_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr))); result += line; } snprintf(line, sizeof(line), " x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end); diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index f4a09f25b..0f5f08a53 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -34,9 +34,9 @@ namespace Inkscape { using Inkscape::Extension::Internal::CairoRenderContext; #endif -class SPStyle; +struct SPStyle; class Shape; -class SPPrintContext; +struct SPPrintContext; class SVGLength; class Path; class SPCurve; diff --git a/src/libnrtype/Makefile_insert b/src/libnrtype/Makefile_insert index 7cd99e1a8..0ce8f1fd5 100644 --- a/src/libnrtype/Makefile_insert +++ b/src/libnrtype/Makefile_insert @@ -11,7 +11,6 @@ ink_common_sources += \ libnrtype/nr-type-pos-def.h \ libnrtype/nr-type-primitives.cpp \ libnrtype/nr-type-primitives.h \ - libnrtype/nrtype-forward.h \ libnrtype/FontFactory.cpp \ libnrtype/FontFactory.h \ libnrtype/FontInstance.cpp \ diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index 710e0b84f..39a04914b 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -2,27 +2,39 @@ # include <config.h> #endif -#include <gtkmm.h> +#include <gtkmm/treemodel.h> +#include <gtkmm/liststore.h> + #include <libnrtype/font-instance.h> #include <libnrtype/TextWrapper.h> #include <libnrtype/one-glyph.h> -#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 "xml/repr.h" + +#include <glibmm/regex.h> + +//#define DEBUG_FONT + namespace Inkscape { FontLister::FontLister () { font_list_store = Gtk::ListStore::create (FontList); - + font_list_store->freeze_notify(); + 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(); @@ -40,7 +52,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; @@ -52,10 +65,898 @@ namespace Inkscape } (*treeModelIter)[FontList.styles] = styles; - - font_list_store_iter_map.insert(std::make_pair(familyName, Gtk::TreePath(treeModelIter))); + (*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" + // bool + // 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.family] << std::endl; + // return false; + // } + // return true; + // } + // font_list_store->foreach_iter( sigc::mem_fun(*this, &FontLister::print_document_font )); + + void + FontLister::update_font_list( SPDocument* document ) { + + SPObject *r = document->getRoot(); + if( !r ) { + return; + } + + 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.family] << std::endl; + iter = font_list_store->erase( iter ); + } else { + // std::cout << " First on system: " << row[FontList.family] << std::endl; + break; + } + } + + /* Get "font-family"s used in document. */ + std::list<Glib::ustring> fontfamilies; + update_font_list_recursive( r, &fontfamilies ); + + fontfamilies.sort(); + fontfamilies.unique(); + fontfamilies.reverse(); + + /* Insert separator */ + if( !fontfamilies.empty() ) { + Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); + (*treeModelIter)[FontList.family] = "#"; + (*treeModelIter)[FontList.onSystem] = false; + } + + /* Insert font-family's in document. */ + std::list<Glib::ustring>::iterator i; + for( i = fontfamilies.begin(); i != fontfamilies.end(); ++i) { + + GList *styles = default_styles; + + /* See if font-family (or first in fallback list) is on system. If so, get styles. */ + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple(",", *i ); + if( !tokens[0].empty() ) { + + 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.family] ) == 0 ) { + styles = row[FontList.styles]; + break; + } + ++iter2; + } + } + + Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); + (*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(); + } + + void + FontLister::update_font_list_recursive( SPObject *r, std::list<Glib::ustring> *l ) { + + const gchar *style = r->getRepr()->attribute("style"); + if( style != NULL ) { + + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple(";", style ); + for( size_t i=0; i < tokens.size(); ++i ) { + + Glib::ustring token = tokens[i]; + size_t found = token.find("font-family:"); + + if( found != Glib::ustring::npos ) { + + // Remove "font-family:" + token.erase(found,12); + + // Remove any leading single or double quote + if( token[0] == '\'' || token[0] == '"' ) { + token.erase(0,1); + } + + // Remove any trailing single or double quote + if( token[token.length()-1] == '\'' || token[token.length()-1] == '"' ) { + token.erase(token.length()-1); + } + + l->push_back( token ); + } + } + } + + for (SPObject *child = r->firstChild(); child; child = child->getNext()) { + update_font_list_recursive( child, l ); + } + } + + 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 BUG... + // A font spec of Delicious, 500 Italic should result in a family of 'Delicious' + // and a style of 'Medium Italic'. It results instead with: a family of + // 'Delicious, 500' with a style of 'Medium Italic'. We chop of any weight numbers + // at the end of the family: match ",[1-9]00^". + Glib::RefPtr<Glib::Regex> weight = Glib::Regex::create(",[1-9]00$"); + Family = weight->replace( Family, 0, "", Glib::REGEX_MATCH_PARTIAL ); + + // 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; + + Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); + while( iter != font_list_store->children().end() ) { + + Gtk::TreeModel::Row row = *iter; + + if( family.compare( row[FontList.family] ) == 0 ) { + return row; + } + + ++iter; + } + + 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 () @@ -67,8 +968,106 @@ namespace Inkscape { return font_list_store; } + + const Glib::RefPtr<Gtk::ListStore> + FontLister::get_style_list () const + { + return style_list_store; + } } +// Helper functions + +// Separator function (if true, a separator will be drawn) +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.family + return (text && strcmp(text,"#") == 0); +} + +void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer /*data*/) +{ + gchar *family; + gboolean onSystem = false; + gtk_tree_model_get(model, iter, 0, &family, 2, &onSystem, -1); + Glib::ustring family_escaped = g_markup_escape_text(family, -1); + //g_free(family); + Glib::ustring markup; + if( !onSystem ) { + markup = "<span foreground='darkblue'>"; + /* See if font-family on system */ + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s*,\\s*", family_escaped ); + for( size_t i=0; i < tokens.size(); ++i ) { + Glib::ustring token = tokens[i]; + + GtkTreeIter iter; + gboolean valid; + gchar *family = 0; + gboolean onSystem = true; + gboolean found = false; + for( valid = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(model), &iter ); + valid; + valid = gtk_tree_model_iter_next( GTK_TREE_MODEL(model), &iter ) ) { + + gtk_tree_model_get(model, &iter, 0, &family, 2, &onSystem, -1); + if( onSystem && token.compare( family ) == 0 ) { + found = true; + break; + } + } + if( found ) { + markup += g_markup_escape_text(token.c_str(), -1); + markup += ", "; + } else { + markup += "<span strikethrough=\"true\" strikethrough_color=\"red\">"; + markup += g_markup_escape_text(token.c_str(), -1); + markup += "</span>"; + markup += ", "; + } + } + // Remove extra comma and space from end. + if( markup.size() >= 2 ) { + markup.resize( markup.size()-2 ); + } + markup += "</span>"; + // std::cout << markup << std::endl; + } else { + markup = family_escaped; + } + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1); + if (show_sample) { + + Glib::ustring sample = prefs->getString("/tools/text/font_sample"); + Glib::ustring sample_escaped = g_markup_escape_text(sample.data(), -1); + + markup += " <span foreground='gray' font_family='"; + markup += family_escaped; + markup += "'>"; + markup += sample_escaped; + markup += "</span>"; + } + + g_object_set (G_OBJECT (cell), "markup", markup.c_str(), NULL); +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/libnrtype/font-lister.h b/src/libnrtype/font-lister.h index c9ab7b21d..5a8f578d9 100644 --- a/src/libnrtype/font-lister.h +++ b/src/libnrtype/font-lister.h @@ -7,9 +7,11 @@ * Authors: * Chris Lahey <clahey@ximian.com> * Lauris Kaplinski <lauris@kaplinski.com> + * Tavmjong Bah <tavmjong@free.fr> * * Copyright (C) 1999-2001 Ximian, Inc. * Copyright (C) 2002 Lauris Kaplinski + * Copyright (C) 2013 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -21,11 +23,37 @@ #include <glibmm/ustring.h> #include "nr-type-primitives.h" +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 { @@ -33,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 @@ -47,38 +76,43 @@ 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; + /** Column containing flag if font is on system + */ + Gtk::TreeModelColumn<gboolean> onSystem; + FontListClass () { - add (font); + add (family); add (styles); + add (onSystem); } }; - /* Case-insensitive < compare for standard strings */ - class StringLessThan + FontListClass FontList; + + class FontStyleListClass + : public Gtk::TreeModelColumnRecord { - public: - bool operator () (std::string str1, std::string str2) const - { - std::string s1=str1; // Can't transform the originals! - std::string s2=str2; - std::transform(s1.begin(), s1.end(), s1.begin(), (int(*)(int)) toupper); - std::transform(s2.begin(), s2.end(), s2.begin(), (int(*)(int)) toupper); - return s1<s2; - } + public: + /** Column containing the styles + */ + Gtk::TreeModelColumn<Glib::ustring> styles; + + FontStyleListClass () + { + add (styles); + } }; - FontListClass FontList; - typedef std::map<Glib::ustring, Gtk::TreePath, StringLessThan> IterMapType; + FontStyleListClass FontStyleList; - /** Returns the ListStore with the font names + /** 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 @@ -87,28 +121,152 @@ 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 + * + */ + void + update_font_list ( SPDocument* document); + + private: + void + update_font_list_recursive( SPObject *r, std::list<Glib::ustring> *l ); + + public: static Inkscape::FontLister* get_instance () { - static Inkscape::FontLister* instance = new Inkscape::FontLister(); + static Inkscape::FontLister* instance = new Inkscape::FontLister(); return instance; } - Gtk::TreePath - get_row_for_font (Glib::ustring family) + /** 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 () { - IterMapType::iterator iter = font_list_store_iter_map.find (family); - if (iter == font_list_store_iter_map.end ()) throw FAMILY_NOT_FOUND; - return (*iter).second; + 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 { return families; } - private: FontLister (); @@ -116,11 +274,38 @@ namespace Inkscape NRNameList families; Glib::RefPtr<Gtk::ListStore> font_list_store; - IterMapType font_list_store_iter_map; + 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; }; } +// Helper functions +gboolean font_lister_separator_func(GtkTreeModel *model, + GtkTreeIter *iter, + gpointer /*data*/); + +void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer /*data*/); + #endif /* diff --git a/src/libnrtype/font-style-to-pos.h b/src/libnrtype/font-style-to-pos.h index 41ba6cf72..56eb391c2 100644 --- a/src/libnrtype/font-style-to-pos.h +++ b/src/libnrtype/font-style-to-pos.h @@ -3,7 +3,7 @@ #include <libnrtype/nr-type-pos-def.h> -class SPStyle; +struct SPStyle; NRTypePosDef font_style_to_pos(SPStyle const &style); |
