summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
authorMarkus Engel <markus.engel@tum.de>2013-03-29 23:52:42 +0000
committerMarkus Engel <markus.engel@tum.de>2013-03-29 23:52:42 +0000
commita168040d5a452544328a1e6ad35aaac351f94d44 (patch)
treefae1ba829f543a473da281bd5fa6e4deabbf6912 /src/libnrtype
parentRemoved function pointers from SPObject and subclasses. (diff)
parentDutch translation update (diff)
downloadinkscape-a168040d5a452544328a1e6ad35aaac351f94d44.tar.gz
inkscape-a168040d5a452544328a1e6ad35aaac351f94d44.zip
merged from trunk
(bzr r11608.1.56)
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontFactory.cpp105
-rw-r--r--src/libnrtype/FontFactory.h3
-rw-r--r--src/libnrtype/FontInstance.cpp18
-rw-r--r--src/libnrtype/Layout-TNG-Input.cpp6
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp11
-rw-r--r--src/libnrtype/Layout-TNG.h4
-rw-r--r--src/libnrtype/Makefile_insert1
-rw-r--r--src/libnrtype/font-lister.cpp1019
-rw-r--r--src/libnrtype/font-lister.h243
-rw-r--r--src/libnrtype/font-style-to-pos.h2
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);