summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontFactory.cpp150
-rw-r--r--src/libnrtype/FontFactory.h6
-rw-r--r--src/libnrtype/FontInstance.cpp107
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp235
-rw-r--r--src/libnrtype/Layout-TNG-Input.cpp20
-rw-r--r--src/libnrtype/Layout-TNG-OutIter.cpp28
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp372
-rw-r--r--src/libnrtype/Layout-TNG.cpp5
-rw-r--r--src/libnrtype/Layout-TNG.h25
-rw-r--r--src/libnrtype/Makefile_insert1
-rw-r--r--src/libnrtype/TextWrapper.cpp86
-rw-r--r--src/libnrtype/font-instance.h4
-rw-r--r--src/libnrtype/font-lister.cpp1026
-rw-r--r--src/libnrtype/font-lister.h243
-rw-r--r--src/libnrtype/font-style-to-pos.h2
15 files changed, 1998 insertions, 312 deletions
diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp
index 63f9bd6b0..7c0b4ffba 100644
--- a/src/libnrtype/FontFactory.cpp
+++ b/src/libnrtype/FontFactory.cpp
@@ -8,34 +8,26 @@
*
*/
-#define PANGO_ENABLE_ENGINE
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <glibmm.h>
-#include <glib.h>
-#include <glibmm/i18n.h> // _()
+#ifndef PANGO_ENABLE_ENGINE
+#define PANGO_ENABLE_ENGINE
+#endif
+
+#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 +41,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 +268,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 +277,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;
@@ -304,7 +296,7 @@ font_factory *font_factory::Default(void)
}
font_factory::font_factory(void) :
- nbEnt(0),
+ nbEnt(0), // Note: this "ents" cache only keeps fonts from being unreffed, does not speed up access
maxEnt(32),
ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))),
@@ -322,20 +314,20 @@ 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
}
font_factory::~font_factory(void)
{
- if (loadedPtr) {
- FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr);
- delete tmp;
- loadedPtr = 0;
- }
-
for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
if ( ents ) g_free(ents);
@@ -347,11 +339,17 @@ font_factory::~font_factory(void)
#endif
//g_object_unref(fontContext);
+ if (loadedPtr) {
+ FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr);
+ delete tmp;
+ loadedPtr = 0;
+ }
+
// Delete the pango font pointers in the string to instance map
PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
while (it != fontInstanceMap.end()) {
pango_font_description_free((*it).second);
- it++;
+ ++it;
}
}
@@ -396,6 +394,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 +429,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 +481,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 +491,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
@@ -629,7 +663,7 @@ Glib::ustring font_factory::FontSpecificationBestMatch(const Glib::ustring & fon
Glib::ustring bestMatchDescription;
bool setFirstFamilyMatch = false;
- for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
+ for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); ++it) {
Glib::ustring currentFontSpec = (*it).first;
Glib::ustring currentFamily = GetUIFamilyString((*it).second);
@@ -711,12 +745,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>()));
}
@@ -729,7 +777,7 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
for (std::list<Glib::ustring>::iterator it=styleList.begin();
it != styleList.end();
- it++) {
+ ++it) {
if (*it == styleUIName) {
exists = true;
break;
@@ -746,6 +794,7 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
ConstructFontSpecification(faceDescr)));
fontInstanceMap.insert(
std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
+
} else {
pango_font_description_free(faceDescr);
}
@@ -761,7 +810,7 @@ void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
families = 0;
// Sort the style lists
- for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
+ for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); ++iter) {
(*iter).second.sort(StyleNameCompareInternal);
}
}
@@ -816,7 +865,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 +911,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,11 +959,11 @@ 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 {
- g_warning(_("Ignoring font without family that will crash Pango"));
+ g_warning("%s", _("Ignoring font without family that will crash Pango"));
}
if ( nFace ) {
@@ -919,19 +975,19 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
// point) whether loadedFaces[that_descr] is free or not (and overwriting
// an entry will bring deallocation problems)
res->descr = pango_font_description_copy(descr);
- res->daddy = this;
+ res->parent = this;
res->InstallFace(nFace);
if ( res->pFont == NULL ) {
// failed to install face -> bitmap font
// printf("face failed\n");
- res->daddy = NULL;
+ res->parent = NULL;
delete res;
res = NULL;
if ( canFail ) {
char *tc = pango_font_description_to_string(descr);
- PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
+ PANGO_DEBUG("falling back from %s to 'sans-serif' because InstallFace failed\n",tc);
g_free(tc);
- pango_font_description_set_family(descr,"Sans");
+ pango_font_description_set_family(descr,"sans-serif");
res = Face(descr,false);
}
} else {
@@ -942,9 +998,9 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
} else {
// no match
if ( canFail ) {
- PANGO_DEBUG("falling back to Sans\n");
+ PANGO_DEBUG("falling back to 'sans-serif'\n");
descr = pango_font_description_new();
- pango_font_description_set_family(descr,"Sans");
+ pango_font_description_set_family(descr,"sans-serif");
res = Face(descr,false);
pango_font_description_free(descr);
}
@@ -1071,7 +1127,7 @@ void font_factory::AddInCache(font_instance *who)
return;
}
who->Ref();
- if ( nbEnt == maxEnt ) {
+ if ( nbEnt == maxEnt ) { // cache is filled, unref the oldest-accessed font in it
int bi = 0;
double ba = ents[bi].age;
for (int i = 1;i < nbEnt;i++) {
diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h
index 42f975ab7..7b606d200 100644
--- a/src/libnrtype/FontFactory.h
+++ b/src/libnrtype/FontFactory.h
@@ -30,7 +30,8 @@
#include <pango/pangowin32.h>
#else
#include <pango/pangoft2.h>
-#include <freetype/freetype.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
#endif
@@ -54,6 +55,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..20eca6740 100644
--- a/src/libnrtype/FontInstance.cpp
+++ b/src/libnrtype/FontInstance.cpp
@@ -8,12 +8,14 @@
*
*/
-#define PANGO_ENABLE_ENGINE
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+#ifndef PANGO_ENABLE_ENGINE
+#define PANGO_ENABLE_ENGINE
+#endif
+
#include <ft2build.h>
#include FT_OUTLINE_H
#include FT_BBOX_H
@@ -21,19 +23,12 @@
#include FT_TRUETYPE_TABLES_H
#include <pango/pangoft2.h>
#include <2geom/pathvector.h>
-#include <2geom/svg-path.h>
+#include <2geom/path-sink.h>
#include "libnrtype/font-glyph.h"
#include "libnrtype/font-instance.h"
#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;
@@ -181,7 +176,7 @@ font_instance::font_instance(void) :
pFont(0),
descr(0),
refCount(0),
- daddy(0),
+ parent(0),
nbGlyph(0),
maxGlyph(0),
glyphs(0),
@@ -192,13 +187,14 @@ font_instance::font_instance(void) :
font_instance::~font_instance(void)
{
- if ( daddy ) {
- daddy->UnrefFace(this);
- daddy = 0;
+ if ( parent ) {
+ parent->UnrefFace(this);
+ parent = 0;
}
//printf("font instance death\n");
if ( pFont ) {
+ FreeTheFace();
g_object_unref(pFont);
pFont = 0;
}
@@ -239,10 +235,6 @@ void font_instance::Unref(void)
//printf("font %x %s unref'd %i\n",this,tc,refCount);
//free(tc);
if ( refCount <= 0 ) {
- if ( daddy ) {
- daddy->UnrefFace(this);
- }
- daddy=NULL;
delete this;
}
}
@@ -292,14 +284,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);
@@ -394,30 +386,34 @@ unsigned int font_instance::Attribute(const gchar *key, gchar *str, unsigned int
void font_instance::InitTheFace()
{
+ if (theFace == NULL && pFont != NULL) {
#ifdef USE_PANGO_WIN32
if ( !theFace ) {
LOGFONT *lf=pango_win32_font_logfont(pFont);
g_assert(lf != NULL);
- theFace=pango_win32_font_cache_load(daddy->pangoFontCache,lf);
+ theFace=pango_win32_font_cache_load(parent->pangoFontCache,lf);
g_free(lf);
}
XFORM identity = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
- SetWorldTransform(daddy->hScreenDC, &identity);
- SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE);
- SelectObject(daddy->hScreenDC,theFace);
+ SetWorldTransform(parent->hScreenDC, &identity);
+ SetGraphicsMode(parent->hScreenDC, GM_COMPATIBLE);
+ SelectObject(parent->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);
}
#endif
+ }
}
void font_instance::FreeTheFace()
{
#ifdef USE_PANGO_WIN32
- SelectObject(daddy->hScreenDC,GetStockObject(SYSTEM_FONT));
- pango_win32_font_cache_unload(daddy->pangoFontCache,theFace);
+ SelectObject(parent->hScreenDC,GetStockObject(SYSTEM_FONT));
+ pango_win32_font_cache_unload(parent->pangoFontCache,theFace);
+#else
+ pango_fc_font_unlock_face(PANGO_FC_FONT(pFont));
#endif
theFace=NULL;
}
@@ -428,6 +424,7 @@ void font_instance::InstallFace(PangoFont* iFace)
return;
}
pFont=iFace;
+ iFace = NULL;
InitTheFace();
@@ -448,7 +445,7 @@ bool font_instance::IsOutlineFont(void)
InitTheFace();
#ifdef USE_PANGO_WIN32
TEXTMETRIC tm;
- return GetTextMetrics(daddy->hScreenDC,&tm) && tm.tmPitchAndFamily&(TMPF_TRUETYPE|TMPF_DEVICE);
+ return GetTextMetrics(parent->hScreenDC,&tm) && tm.tmPitchAndFamily&(TMPF_TRUETYPE|TMPF_DEVICE);
#else
return FT_IS_SCALABLE(theFace);
#endif
@@ -461,12 +458,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;
@@ -517,10 +515,10 @@ void font_instance::LoadGlyph(int glyph_id)
MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
OUTLINETEXTMETRIC otm;
- GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm);
+ GetOutlineTextMetrics(parent->hScreenDC, sizeof(otm), &otm);
GLYPHMETRICS metrics;
- DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
- double scale=1.0/daddy->fontSize;
+ DWORD bufferSize=GetGlyphOutline (parent->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
+ double scale=1.0/parent->fontSize;
n_g.h_advance=metrics.gmCellIncX*scale;
n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
n_g.h_width=metrics.gmBlackBoxX*scale;
@@ -532,7 +530,7 @@ void font_instance::LoadGlyph(int glyph_id)
doAdd=true;
} else {
char *buffer = new char[bufferSize];
- if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) {
+ if ( GetGlyphOutline (parent->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) {
// shit happened
} else {
// Platform SDK is rubbish, read KB87115 instead
@@ -622,7 +620,7 @@ void font_instance::LoadGlyph(int glyph_id)
doAdd=true;
}
#endif
- path_builder.finish();
+ path_builder.flush();
if ( doAdd ) {
Geom::PathVector pv = path_builder.peek();
@@ -659,10 +657,10 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
}
#ifdef USE_PANGO_WIN32
OUTLINETEXTMETRIC otm;
- if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) {
+ if ( !GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) {
return false;
}
- double scale=1.0/daddy->fontSize;
+ double scale=1.0/parent->fontSize;
ascent=fabs(otm.otmAscent*scale);
descent=fabs(otm.otmDescent*scale);
leading=fabs(otm.otmLineGap*scale);
@@ -679,6 +677,41 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
return true;
}
+bool font_instance::FontDecoration(
+ double &underline_position, double &underline_thickness,
+ double &linethrough_position, double &linethrough_thickness
+){
+ if ( pFont == NULL ) {
+ return false;
+ }
+ InitTheFace();
+ if ( theFace == NULL ) {
+ return false;
+ }
+#ifdef USE_PANGO_WIN32
+ OUTLINETEXTMETRIC otm;
+ if ( !GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) {
+ return false;
+ }
+ double scale=1.0/parent->fontSize;
+ underline_position = fabs(otm.otmUnderscorePosition *scale);
+ underline_thickness = fabs(otm.otmUnderscoreSize *scale);
+ linethrough_position = fabs(otm.otmStrikeoutPosition *scale);
+ linethrough_thickness = fabs(otm.otmStrikeoutSize *scale);
+#else
+ if ( theFace->units_per_EM == 0 ) {
+ return false; // bitmap font
+ }
+ underline_position = fabs(((double)theFace->underline_position )/((double)theFace->units_per_EM));
+ underline_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM));
+ // there is no specific linethrough information, mock it up from other font fields
+ linethrough_position = fabs(((double)theFace->ascender / 3.0 )/((double)theFace->units_per_EM));
+ linethrough_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM));
+#endif
+ return true;
+}
+
+
bool font_instance::FontSlope(double &run, double &rise)
{
run = 0.0;
@@ -694,7 +727,7 @@ bool font_instance::FontSlope(double &run, double &rise)
#ifdef USE_PANGO_WIN32
OUTLINETEXTMETRIC otm;
- if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) return false;
+ if ( !GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) return false;
run=otm.otmsCharSlopeRun;
rise=otm.otmsCharSlopeRise;
#else
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index 7e684e7e3..973db0165 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -120,8 +120,8 @@ class Layout::Calculator
BrokenSpan. */
struct UnbrokenSpan {
PangoGlyphString *glyph_string;
- int pango_item_index; /// index into _para.pango_items, or -1 if this is style only
- unsigned input_index; /// index into Layout::_input_stream
+ int pango_item_index; /// index into _para.pango_items, or -1 if this is style only
+ unsigned input_index; /// index into Layout::_input_stream
Glib::ustring::const_iterator input_stream_first_character;
double font_size;
LineHeight line_height; /// This is not the CSS line-height attribute!
@@ -213,6 +213,43 @@ class Layout::Calculator
/* *********************************************************************************************************/
// Per-line functions
+/**
+ * For debugging, not called in distributed code
+ *
+ * Input: para->first_input_index, para->pango_items
+ */
+static void dumpPangoItemsOut(ParagraphInfo *para){
+ std::cout << "Pango items: " << para->pango_items.size() << std::endl;
+ for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){
+ std::cout
+ << "idx: " << pidx
+ << " offset: "
+ << para->pango_items[pidx].item->offset
+ << " length: "
+ << para->pango_items[pidx].item->length
+ << std::endl;
+ }
+}
+
+/**
+ * For debugging, not called in distributed code
+ *
+ * Input: para->first_input_index, para->pango_items
+ */
+static void dumpUnbrokenSpans(ParagraphInfo *para){
+ std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl;
+ for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){
+ std::cout
+ << "idx: " << uidx
+ << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index
+ << " input_index: " << para->unbroken_spans[uidx].input_index
+ << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para
+ << " text_bytes: " << para->unbroken_spans[uidx].text_bytes
+ << std::endl;
+ }
+}
+
+
bool _goToNextWrapShape();
@@ -246,8 +283,10 @@ class Layout::Calculator
{
span->setZero();
- if (span->start.iter_span->dx._set && span->start.char_byte == 0)
- span->width += span->start.iter_span->dx.computed;
+ if (span->start.iter_span->dx._set && span->start.char_byte == 0){
+ if(para.direction == RIGHT_TO_LEFT){ span->width -= span->start.iter_span->dx.computed; }
+ else { span->width += span->start.iter_span->dx.computed; }
+ }
if (span->start.iter_span->pango_item_index == -1) {
// if this is a style-only span there's no text in it
@@ -510,15 +549,19 @@ class Layout::Calculator
x = 0.0;
} else {
direction_sign = -1.0;
- if (para.alignment == FULL && !_flow._input_wrap_shapes.empty())
+ if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
x = it_chunk->scanrun_width;
- else
+ }
+ else {
x = it_chunk->text_width;
+ }
}
for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) {
// begin adding spans to the list
UnbrokenSpan const &unbroken_span = *it_span->start.iter_span;
+ double x_in_span_last = 0.0; // set at the END when a new cluster starts
+ double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts.
if (it_span->start.char_byte == 0) {
// Start of an unbroken span, we might have dx, dy or rotate still to process
@@ -535,7 +578,6 @@ class Layout::Calculator
}
Layout::Span new_span;
- double x_in_span = 0.0;
new_span.in_chunk = _flow._chunks.size() - 1;
new_span.line_height = unbroken_span.line_height;
@@ -581,14 +623,19 @@ class Layout::Calculator
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]);
Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ;
unsigned char_index_in_unbroken_span = it_span->start.char_index;
- unsigned cluster_start_char_index = _flow._characters.size();
- double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier);
+ double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier);
+ int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster
+ int log_cluster_size_chars = 0; // Number of characters in this log_cluster
+ unsigned end_byte = 0;
for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) {
- unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
- if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start)
- cluster_start_char_index = _flow._characters.size();
-
+ unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
+ int newcluster = 0;
+ if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){
+ newcluster = 1;
+ x_in_span = x_in_span_last;
+ }
+
if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes
&& *iter_source_text == UNICODE_SOFT_HYPHEN
&& glyph_index + 1 != it_span->end_glyph_index) {
@@ -611,7 +658,7 @@ class Layout::Calculator
// create the Layout::Glyph
Layout::Glyph new_glyph;
new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph;
- new_glyph.in_character = cluster_start_char_index;
+ new_glyph.in_character = _flow._characters.size();
new_glyph.rotation = glyph_rotate;
/* put something like this back in when we do glyph-rotation-horizontal/vertical
@@ -652,28 +699,42 @@ class Layout::Calculator
}
new_glyph.x -= cluster_width;
}
- _flow._glyphs.push_back(new_glyph);
+ _flow._glyphs.push_back(new_glyph);
// create the Layout::Character(s)
double advance_width = new_glyph.width;
- unsigned end_byte;
- if (glyph_index == (unsigned)unbroken_span.glyph_string->num_glyphs - 1)
- end_byte = it_span->start.iter_span->text_bytes;
- else {
- // output chars for the whole cluster that is commenced by this glyph
- if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start) {
- int next_cluster_glyph_index = glyph_index + 1;
- while (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs
- && !unbroken_span.glyph_string->glyphs[next_cluster_glyph_index].attr.is_cluster_start)
- next_cluster_glyph_index++;
- if (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs)
- end_byte = unbroken_span.glyph_string->log_clusters[next_cluster_glyph_index];
- else
- end_byte = it_span->start.iter_span->text_bytes;
- } else
- end_byte = char_byte; // don't output any chars if we're not at the start of a cluster
+ if (newcluster){
+ // find where the text ends for this log_cluster
+ end_byte = it_span->start.iter_span->text_bytes; // Upper limit
+ for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){
+ if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){
+ end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index];
+ break;
+ }
+ }
+ // Figure out how many glyphs and characters are in the log_cluster.
+ log_cluster_size_glyphs = 0;
+ log_cluster_size_chars = 0;
+ for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){
+ if(unbroken_span.glyph_string->log_clusters[glyph_index ] !=
+ unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break;
+ }
+ Glib::ustring::const_iterator lclist = iter_source_text;
+ unsigned lcb = char_byte;
+ while(lcb < end_byte){
+ log_cluster_size_chars++;
+ lclist++;
+ lcb = lclist.base() - unbroken_span.input_stream_first_character.base();
+ }
}
while (char_byte < end_byte) {
+ /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining.
+ When there are no ligatures these two sizes are always the same.
+ */
+ if(log_cluster_size_chars < log_cluster_size_glyphs){
+ log_cluster_size_glyphs--;
+ break;
+ }
Layout::Character new_character;
new_character.in_span = _flow._spans.size();
new_character.x = x_in_span;
@@ -687,23 +748,27 @@ class Layout::Calculator
iter_source_text++;
char_index_in_unbroken_span++;
char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base();
+ log_cluster_size_chars--;
}
- advance_width *= direction_sign;
- if (new_span.direction != para.direction) {
- counter_directional_width_remaining -= advance_width;
- x -= advance_width;
- x_in_span -= advance_width;
- } else {
- x += advance_width;
- x_in_span += advance_width;
+ if (newcluster){
+ advance_width *= direction_sign;
+ if (new_span.direction != para.direction) {
+ counter_directional_width_remaining -= advance_width;
+ x -= advance_width;
+ x_in_span_last -= advance_width;
+ } else {
+ x += advance_width;
+ x_in_span_last += advance_width;
+ }
}
+ newcluster = 0;
}
} else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
}
- new_span.x_end = new_span.x_start + x_in_span;
+ new_span.x_end = new_span.x_start + x_in_span_last;
_flow._spans.push_back(new_span);
previous_direction = new_span.direction;
}
@@ -879,8 +944,9 @@ void Layout::Calculator::ParagraphInfo::free()
*
* Input: para.first_input_index.
* Output: para.direction, para.pango_items, para.char_attributes.
+ * Returns: the number of spans created by pango_itemize
*/
-void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const
+void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const
{
Glib::ustring para_text;
PangoAttrList *attributes_list;
@@ -914,6 +980,7 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) cons
attribute_font_description->end_index = para_text.bytes();
pango_attr_list_insert(attributes_list, attribute_font_description);
// ownership of attribute is assumed by the list
+ font->Unref();
}
}
@@ -974,8 +1041,9 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
line_height->setZero();
*line_height_multiplier = 1.0;
}
- else
+ else {
font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
+ }
*line_height *= font_size;
// yet another borked SPStyle member that we're going to have to fix ourselves
@@ -1008,6 +1076,11 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font
*line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total();
}
+bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b)
+{
+ return (a.geometry.width > b.geometry.width);
+}
+
/**
* Split the paragraph into spans. Also call pango_shape() on them.
@@ -1034,13 +1107,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
break; // stop at the end of the paragraph
else if (control_code->code == ARBITRARY_GAP) {
UnbrokenSpan new_span;
- new_span.pango_item_index = -1;
- new_span.input_index = input_index;
- new_span.line_height.ascent = control_code->ascent;
+ new_span.pango_item_index = -1;
+ new_span.input_index = input_index;
+ new_span.line_height.ascent = control_code->ascent;
new_span.line_height.descent = control_code->descent;
new_span.line_height.leading = 0.0;
- new_span.text_bytes = 0;
- new_span.char_index_in_para = char_index_in_para;
+ new_span.text_bytes = 0;
+ new_span.char_index_in_para = char_index_in_para;
para->unbroken_spans.push_back(new_span);
TRACE(("add gap span %d\n", para->unbroken_spans.size() - 1));
}
@@ -1123,6 +1196,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() );
g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes))
== NULL );
+ /* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating
+ them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base
+ and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map
+ from glyph to first character of the log_cluster range. This destroys the 1:1 correspondence between
+ characters and glyphs. A big chunk of the conditional code which immediately follows this call
+ is there to clean up the resulting mess.
+ */
pango_shape(text_source->text->data() + span_start_byte_in_source,
new_span.text_bytes,
&para->pango_items[pango_item_index].item->analysis,
@@ -1134,24 +1214,55 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const
// let's reverse the glyphstring on a cluster-by-cluster basis
const unsigned nglyphs = new_span.glyph_string->num_glyphs;
std::vector<PangoGlyphInfo> infos(nglyphs);
- std::vector<gint> clusters(nglyphs);
- unsigned i, cluster_start = 0;
-
- for (i = 0 ; i < nglyphs ; ++i) {
- if (new_span.glyph_string->glyphs[i].attr.is_cluster_start) {
- if (i != cluster_start) {
- std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
- std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
- }
- cluster_start = i;
+ std::vector<gint> clusters(nglyphs);
+ unsigned i, j;
+ for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0;
+ for (i = 0 ; i < nglyphs ; i++) {
+ j=i;
+ while( (j < nglyphs-1) &&
+ (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i])
+ )j++;
+ /*
+ CAREFUL, within a log_cluster the order of glyphs may not map 1:1, or
+ even in the same order, to the original unicode characters!!! Among
+ other things, diacritical mark glyphs can end up in front of the base
+ character. That makes determining kerning, even approximately, difficult
+ later on. To resolve this somewhat sort the glyphs with the same
+ log_cluster into descending order by width. In theory there should be 1
+ that is nonzero, and N that are zero. The order of the zero width ones
+ does not matter. Sort the glyphs before copying. If ligatures other than with
+ Mark, nonspacing are ever implemented in Pango this will screw up, for instance
+ changing "fi" to "if".
+ */
+ if(j - i){
+ std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth);
}
- }
- if (i != cluster_start) {
- std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i);
- std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i);
+
+ new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1;
+ std::copy(&new_span.glyph_string->glyphs[ i], &new_span.glyph_string->glyphs[ j+1], infos.end() - j -1);
+ std::copy(&new_span.glyph_string->log_clusters[i], &new_span.glyph_string->log_clusters[j+1], clusters.end() - j -1);
+ i = j;
}
std::copy(infos.begin(), infos.end(), new_span.glyph_string->glyphs);
std::copy(clusters.begin(), clusters.end(), new_span.glyph_string->log_clusters);
+ /* glyphs[].x_offset values are probably out of order within any log_clusters, apparently harmless */
+ }
+ else { // ltr sections are in order but glyphs in a log_cluster following a ligature may not be. Sort, but no block swapping.
+ const unsigned nglyphs = new_span.glyph_string->num_glyphs;
+ unsigned i, j;
+ for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0;
+ for (i = 0 ; i < nglyphs ; i++) {
+ j=i;
+ while( (j < nglyphs-1) &&
+ (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i])
+ )j++;
+ if(j - i){
+ std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth);
+ }
+ new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1;
+ i = j;
+ }
+ /* glyphs[].x_offset values may be out of order within any log_clusters, apparently harmless */
}
new_span.pango_item_index = pango_item_index;
_computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier);
@@ -1469,7 +1580,7 @@ bool Layout::Calculator::calculate()
if (_scanline_maker == NULL)
break; // we're trying to flow past the last wrap shape
- _buildPangoItemizationForPara(&para);
+ _buildPangoItemizationForPara(&para);
unsigned para_end_input_index = _buildSpansForPara(&para);
if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE)
diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp
index c5ea3969d..cb3e6f620 100644
--- a/src/libnrtype/Layout-TNG-Input.cpp
+++ b/src/libnrtype/Layout-TNG-Input.cpp
@@ -9,7 +9,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef PANGO_ENABLE_ENGINE
#define PANGO_ENABLE_ENGINE
+#endif
#include <gtk/gtk.h>
#include "Layout-TNG.h"
@@ -19,20 +25,16 @@
#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 {
void Layout::_clearInputObjects()
{
- for(std::vector<InputStreamItem*>::iterator it = _input_stream.begin() ; it != _input_stream.end() ; it++)
+ for(std::vector<InputStreamItem*>::iterator it = _input_stream.begin() ; it != _input_stream.end() ; ++it) {
delete *it;
+ }
+
_input_stream.clear();
_input_wrap_shapes.clear();
}
@@ -52,7 +54,7 @@ void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_
sp_style_ref(style);
new_source->text_length = 0;
- for ( ; text_begin != text_end && text_begin != text.end() ; text_begin++)
+ for ( ; text_begin != text_end && text_begin != text.end() ; ++text_begin)
new_source->text_length++; // save this because calculating the length of a UTF-8 string is expensive
if (optional_attributes) {
@@ -291,7 +293,7 @@ PangoFontDescription *Layout::InputStreamTextSource::styleGetFontDescription() c
// pango, so it's not the limiting factor
Glib::ustring family;
if (style->text->font_family.value == NULL) {
- family = "Sans";
+ family = "sans-serif";
} else {
gchar **families = g_strsplit(style->text->font_family.value, ",", -1);
if (families) {
diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp
index 4e61c10a0..6dc42d998 100644
--- a/src/libnrtype/Layout-TNG-OutIter.cpp
+++ b/src/libnrtype/Layout-TNG-OutIter.cpp
@@ -168,7 +168,7 @@ Layout::iterator Layout::getLetterAt(double x, double y) const
return end();
}
-Layout::iterator Layout::sourceToIterator(void *source_cookie, Glib::ustring::const_iterator text_iterator) const
+Layout::iterator Layout::sourceToIterator(void *source_cookie /*, Glib::ustring::const_iterator text_iterator*/) const
{
unsigned source_index;
if (_characters.empty()) return end();
@@ -181,7 +181,10 @@ Layout::iterator Layout::sourceToIterator(void *source_cookie, Glib::ustring::co
if (_input_stream[source_index]->Type() != TEXT_SOURCE)
return iterator(this, char_index);
+ return iterator(this, char_index);
+ /* This code was never used, the text_iterator argument was "NULL" in all calling code
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[source_index]);
+
if (text_iterator <= text_source->text_begin) return iterator(this, char_index);
if (text_iterator >= text_source->text_end) {
if (source_index == _input_stream.size() - 1) return end();
@@ -194,11 +197,7 @@ Layout::iterator Layout::sourceToIterator(void *source_cookie, Glib::ustring::co
iter_text++;
}
return end(); // never happens
-}
-
-Layout::iterator Layout::sourceToIterator(void *source_cookie) const
-{
- return sourceToIterator(source_cookie, Glib::ustring::const_iterator(std::string::const_iterator(NULL)));
+ */
}
Geom::OptRect Layout::glyphBoundingBox(iterator const &it, double *rotation) const
@@ -534,19 +533,24 @@ void Layout::getSourceOfCharacter(iterator const &it, void **source_cookie, Glib
InputStreamItem *stream_item = _input_stream[_spans[_characters[it._char_index].in_span].in_input_stream_item];
*source_cookie = stream_item->source_cookie;
if (text_iterator && stream_item->Type() == TEXT_SOURCE) {
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(stream_item);
- Glib::ustring::const_iterator text_iter_const = text_source->text_begin;
+ InputStreamTextSource *text_source = dynamic_cast<InputStreamTextSource *>(stream_item);
+
+ // In order to return a non-const iterator in text_iterator, do the const_cast here.
+ // Note that, although ugly, it is safe because we do not write to *iterator anywhere.
+ Glib::ustring::iterator text_iter = const_cast<Glib::ustring *>(text_source->text)->begin();
+
unsigned char_index = it._char_index;
unsigned original_input_source_index = _spans[_characters[char_index].in_span].in_input_stream_item;
// confusing algorithm because the iterator goes forwards while the index goes backwards.
// It's just that it's faster doing it that way
while (char_index && _spans[_characters[char_index - 1].in_span].in_input_stream_item == original_input_source_index) {
- ++text_iter_const;
+ ++text_iter;
char_index--;
}
- text_source->text->begin().base() + (text_iter_const.base() - text_source->text->begin().base());
- *text_iterator = Glib::ustring::iterator(std::string::iterator(const_cast<char*>(&*text_source->text->begin().base() + (text_iter_const.base() - text_source->text->begin().base()))));
- // the caller owns the string, so they're going to want a non-const iterator
+
+ if (text_iterator) {
+ *text_iterator = text_iter;
+ }
}
}
diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp
index 0ce00e43b..77534e706 100644
--- a/src/libnrtype/Layout-TNG-Output.cpp
+++ b/src/libnrtype/Layout-TNG-Output.cpp
@@ -20,19 +20,13 @@
#include "extension/internal/cairo-render-context.h"
#include "display/curve.h"
#include <2geom/pathvector.h>
+#include <libuemf/symbol_convert.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;
}
}
}
@@ -43,12 +37,55 @@ using Inkscape::Extension::Internal::CairoGlyphInfo;
namespace Inkscape {
namespace Text {
+/*
+ dx array (character widths) and
+ ky (vertical kerning for entire span)
+ rtl (+1 for LTR, -1 RTL)
+
+ are smuggled through to the EMF (ignored by others) as:
+ text<nul>N w1 w2 w3 ...wN<nul>y1 y2 y3 .. yN<nul><nul>
+ The ndx, widths, y kern, and rtl are all 7 characters wide. ndx and rtl are ints, the widths and ky are
+ formatted as ' 6f'.
+*/
+char *smuggle_adxkyrtl_in(const char *string, int ndx, float *adx, float ky, float rtl){
+ int slen = strlen(string);
+ /* holds: string
+ fake terminator (one \0)
+ Number of widths (ndx)
+ series of widths (ndx entries)
+ fake terminator (one \0)
+ y kern value (one float)
+ rtl value (one float)
+ real terminator (two \0)
+ */
+ int newsize=slen + 1 + 7 + 7*ndx + 1 + 7 + 7 + 2;
+ newsize = 8*((7 + newsize)/8); // suppress valgrind messages if it is a multiple of 8 bytes???
+ char *smuggle=(char *)malloc(newsize);
+ strcpy(smuggle,string); // text to pass, includes the first fake terminator
+ char *cptr = smuggle + slen + 1; // immediately after the first fake terminator
+ sprintf(cptr,"%07d",ndx); // number of widths to pass
+ cptr+=7; // advance over ndx
+ for(int i=0; i<ndx ; i++){ // all the widths
+ sprintf(cptr," %6f",adx[i]);
+ cptr+=7; // advance over space + width
+ }
+ *cptr='\0';
+ cptr++; // second fake terminator
+ sprintf(cptr," %6f",ky); // y kern for span
+ cptr+=7; // advance over space + ky
+ sprintf(cptr," %6d",(int) rtl); // rtl multiplier for span
+ cptr+=7; // advance over rtl
+ *cptr++ = '\0'; // Set the real terminators
+ *cptr = '\0';
+ return(smuggle);
+}
+
void Layout::_clearOutputObjects()
{
_paragraphs.clear();
_lines.clear();
_chunks.clear();
- for (std::vector<Span>::iterator it_span = _spans.begin() ; it_span != _spans.end() ; it_span++)
+ for (std::vector<Span>::iterator it_span = _spans.begin() ; it_span != _spans.end() ; ++it_span)
if (it_span->font) it_span->font->Unref();
_spans.clear();
_characters.clear();
@@ -84,23 +121,70 @@ void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) con
void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const
{
int glyph_index = 0;
+ double phase0 = 0.0;
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue;
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
+ text_source->style->text_decoration_data.tspan_width = _spans[span_index].width();
+ text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent();
+ text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent();
+ text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading();
+ if(!span_index ||
+ (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){
+ text_source->style->text_decoration_data.tspan_line_start = true;
+ }
+ else {
+ text_source->style->text_decoration_data.tspan_line_start = false;
+ }
+ if((span_index == _spans.size() -1) ||
+ (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index+1].in_chunk].in_line)){
+ text_source->style->text_decoration_data.tspan_line_end = true;
+ }
+ else {
+ text_source->style->text_decoration_data.tspan_line_end = false;
+ }
+ if(_spans[span_index].font){
+ double underline_thickness, underline_position, line_through_thickness,line_through_position;
+ _spans[span_index].font->FontDecoration(underline_position, underline_thickness, line_through_position, line_through_thickness);
+ text_source->style->text_decoration_data.underline_thickness = underline_thickness;
+ text_source->style->text_decoration_data.underline_position = underline_position;
+ text_source->style->text_decoration_data.line_through_thickness = line_through_thickness;
+ text_source->style->text_decoration_data.line_through_position = line_through_position;
+ }
+ else { // can this case ever occur?
+ text_source->style->text_decoration_data.underline_thickness =
+ text_source->style->text_decoration_data.underline_position =
+ text_source->style->text_decoration_data.line_through_thickness =
+ text_source->style->text_decoration_data.line_through_position = 0.0;
+ }
+
DrawingText *nr_text = new DrawingText(in_arena->drawing());
- nr_text->setStyle(text_source->style);
+ bool first_line_glyph = true;
while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) {
Geom::Affine glyph_matrix;
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
- nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix);
+ if(first_line_glyph && text_source->style->text_decoration_data.tspan_line_start){
+ first_line_glyph = false;
+ phase0 = glyph_matrix.translation()[Geom::X];
+ }
+ // save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase
+ (void) nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix,
+ _glyphs[glyph_index].width,
+ _spans[span_index].line_height.getAscent(),
+ _spans[span_index].line_height.getDescent(),
+ glyph_matrix.translation()[Geom::X] - phase0
+ );
}
glyph_index++;
}
+ nr_text->setStyle(text_source->style);
nr_text->setItemBounds(paintbox);
in_arena->prependChild(nr_text);
+ // Set item bounds without filter enlargement
+ in_arena->setItemBounds(paintbox);
}
}
@@ -130,27 +214,26 @@ Geom::OptRect Layout::bounds(Geom::Affine const &transform, int start, int lengt
return bbox;
}
+/* This version is much simpler than the old one
+*/
void Layout::print(SPPrintContext *ctx,
Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox,
Geom::Affine const &ctm) const
{
- if (_input_stream.empty()) return;
+bool text_to_path = ctx->module->textToPath();
+#define MAX_DX 2048
+float hold_dx[MAX_DX]; // For smuggling dx values (character widths) into print functions, unlikely any simple text output will be longer than this.
- Direction block_progression = _blockProgression();
- bool text_to_path = ctx->module->textToPath();
- for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
- if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
- // invisible glyphs
- unsigned same_character = _glyphs[glyph_index].in_character;
- while (_glyphs[glyph_index].in_character == same_character)
- glyph_index++;
- continue;
- }
- Geom::Affine glyph_matrix;
- Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
- InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
- if (text_to_path || _path_fitted) {
+Geom::Affine glyph_matrix;
+
+ if (_input_stream.empty()) return;
+ if (!_glyphs.size()) return; // yes, this can happen.
+ if (text_to_path || _path_fitted) {
+ for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
+ if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1)continue; //invisible glyphs
+ Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
Geom::PathVector const * pv = span.font->PathVector(_glyphs[glyph_index].glyph);
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
if (pv) {
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
Geom::PathVector temp_pv = (*pv) * glyph_matrix;
@@ -159,9 +242,31 @@ void Layout::print(SPPrintContext *ctx,
if (!text_source->style->stroke.isNone())
sp_print_stroke(ctx, temp_pv, ctm, text_source->style, pbox, dbox, bbox);
}
- glyph_index++;
- } else {
- Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
+ }
+ }
+ else {
+ /* index by characters, referencing glyphs and spans only as needed */
+ double char_x;
+ int doUTN = CanUTN(); // Unicode to Nonunicode translation enabled if true
+ Direction block_progression = _blockProgression();
+ int oldtarget = 0;
+ int ndx = 0;
+ double rtl = 1.0; // 1 L->R, -1 R->L, constant across a span. 1.0 for t->b b->t???
+
+ for (unsigned char_index = 0 ; char_index < _characters.size() ; ) {
+ Glib::ustring text_string; // accumulate text for record in this
+ Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
+ int glyph_index = _characters[char_index].in_glyph;
+ if(glyph_index == -1){ // if the character maps to an invisible glyph we cannot know its geometry, so skip it and move on
+ char_index++;
+ continue;
+ }
+ float ky = _glyphs[glyph_index].y; // For smuggling y kern value for span // same value for all positions in a span
+ unsigned span_index = _characters[char_index].in_span;
+ Span const &span = _spans[span_index];
+ char_x = 0.0;
+ Glib::ustring::const_iterator text_iter = span.input_stream_first_character;
+ InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
glyph_matrix = Geom::Scale(1.0, -1.0) * (Geom::Affine)Geom::Rotate(_glyphs[glyph_index].rotation);
if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
glyph_matrix[4] = span.line(this).baseline_y + span.baseline_shift;
@@ -171,38 +276,116 @@ void Layout::print(SPPrintContext *ctx,
glyph_matrix[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
glyph_matrix[5] = span.line(this).baseline_y + span.baseline_shift;
}
- Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
- unsigned char_index = _glyphs[glyph_index].in_character;
- unsigned original_span = _characters[char_index].in_span;
- while (char_index && _characters[char_index - 1].in_span == original_span) {
- char_index--;
- span_iter++;
+ switch(span.direction){
+ case Layout::TOP_TO_BOTTOM:
+ case Layout::BOTTOM_TO_TOP:
+ case Layout::LEFT_TO_RIGHT: rtl = 1.0; break;
+ case Layout::RIGHT_TO_LEFT: rtl = -1.0; break;
}
+ if(doUTN){
+ oldtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
+ }
+
+ // accumulate a record to write
- // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
- Glib::ustring span_string;
- double char_x = _characters[_glyphs[glyph_index].in_character].x;
- unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
- do {
- span_string += *span_iter;
- span_iter++;
-
- unsigned same_character = _glyphs[glyph_index].in_character;
- while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
- char_x += _glyphs[glyph_index].width;
- glyph_index++;
+ unsigned lc_index = char_index;
+ unsigned hold_iisi = _spans[span_index].in_input_stream_item;
+ int newtarget = 0;
+ while(1){
+ glyph_index = _characters[lc_index].in_glyph;
+ if(glyph_index == -1){ // end of a line within a paragraph, for instance
+ lc_index++;
+ break;
}
- } while (glyph_index < _glyphs.size()
- && _path_fitted == NULL
- && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
- && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-4);
+
+ // always append if here
+ text_string += *text_iter;
+
+ // figure out char widths, used by EMF, not currently used elsewhere
+ double cwidth;
+ if(lc_index == _glyphs[glyph_index].in_character){ // Glyph width is used only for the first character, these may be 0
+ cwidth = rtl * _glyphs[glyph_index].width; // width might be zero
+ }
+ else {
+ cwidth = 0;
+ }
+ char_x += cwidth;
+/*
+std:: cout << "DEBUG Layout::print in while "
+<< " char_index " << char_index
+<< " lc_index " << lc_index
+<< " character " << std::hex << (int) *text_iter << std::dec
+<< " glyph_index " << glyph_index
+<< " glyph_xy " << _glyphs[glyph_index].x << " , " << _glyphs[glyph_index].y
+<< " span_index " << span_index
+<< " hold_iisi " << hold_iisi
+<< std::endl; //DEBUG
+*/
+ if(ndx < MAX_DX){
+ hold_dx[ndx++] = fabs(cwidth);
+ }
+ else { // silently truncate any text line silly enough to be longer than MAX_DX
+ lc_index = _characters.size();
+ break;
+ }
+
+
+ // conditions that prevent this character from joining the record
+ lc_index++;
+ if(lc_index >= _characters.size()) break; // nothing more to process, so it must be the end of the record
+ ++text_iter;
+ if(doUTN)newtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
+ if(newtarget != oldtarget)break; // change in unicode to nonunicode translation status
+ // MUST exit on any major span change, but not on some little events, like a font substitution event irrelvant for the file save
+ unsigned next_span_index = _characters[lc_index].in_span;
+ if(span_index != next_span_index){
+ /* on major changes break out of loop.
+ 1st case usually indicates an entire input line has been processed (out of several in a paragraph)
+ 2nd case usually indicates that a format change within a line (font/size/color/etc) is present.
+ */
+/*
+std:: cout << "DEBUG Layout::print in while --- "
+<< " char_index " << char_index
+<< " lc_index " << lc_index
+<< " cwidth " << cwidth
+<< " _char.x (next) " << (lc_index < _characters.size() ? _characters[lc_index].x : -1)
+<< " char_x (end this)" << char_x
+<< " diff " << fabs(char_x - _characters[lc_index].x)
+<< " oldy " << ky
+<< " nexty " << _glyphs[_characters[lc_index].in_glyph].y
+<< std::endl; //DEBUG
+*/
+ if(hold_iisi != _spans[next_span_index].in_input_stream_item)break; // major change, font, size, color, etc, must exit
+ if(fabs(char_x - _spans[next_span_index].x_start) >= 1e-4)break; // xkerning change
+ if(ky != _glyphs[_characters[lc_index].in_glyph].y)break; // ykerning change
+ /*
+ None of the above? Then this is a minor "pangito", update span_index and keep going.
+ The font used by the display may have failed over, but print does not care and can continue to use
+ whatever was specified in the XML.
+ */
+ span_index = next_span_index;
+ text_iter = _spans[span_index].input_stream_first_character;
+ }
+
+ }
+ // write it
sp_print_bind(ctx, glyph_matrix, 1.0);
- sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
+
+ // the dx array is smuggled through to the EMF driver (ignored by others) as:
+ // text<nul>w1 w2 w3 ...wn<nul><nul>
+ // where the widths are floats 7 characters wide, including the space
+
+ char *smuggle_string=smuggle_adxkyrtl_in(text_string.c_str(),ndx, &hold_dx[0], ky, rtl);
+ sp_print_text(ctx, smuggle_string, g_pos, text_source->style);
+ free(smuggle_string);
sp_print_release(ctx);
+ ndx = 0;
+ char_index = lc_index;
}
}
}
+
#ifdef HAVE_CAIRO_PDF
void Layout::showGlyphs(CairoRenderContext *ctx) const
{
@@ -244,7 +427,7 @@ void Layout::showGlyphs(CairoRenderContext *ctx) const
unsigned original_span = _characters[char_index].in_span;
while (char_index && _characters[char_index - 1].in_span == original_span) {
char_index--;
- span_iter++;
+ ++span_iter;
}
// try to output as many characters as possible in one go
@@ -254,7 +437,7 @@ void Layout::showGlyphs(CairoRenderContext *ctx) const
glyphtext.clear();
do {
span_string += *span_iter;
- span_iter++;
+ ++span_iter;
unsigned same_character = _glyphs[glyph_index].in_character;
while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
@@ -340,54 +523,81 @@ static char const *weight_to_text(PangoWeight w)
return "???";
}
+Glib::ustring Layout::getFontFamily(unsigned span_index) const
+{
+ if (span_index >= _spans.size())
+ return "";
+
+ if (_spans[span_index].font) {
+ return sp_font_description_get_family(_spans[span_index].font->descr);
+ }
+
+ return "";
+}
+
Glib::ustring Layout::dumpAsText() const
{
Glib::ustring result;
+ Glib::ustring::const_iterator icc;
+ char line[256];
+
+ result = Glib::ustring::compose("spans %1\nchars %2\nglyphs %3\n", _spans.size(), _characters.size(), _glyphs.size());
+ if(_characters.size() > 1){
+ unsigned lastspan=5000;
+ for(unsigned j = 0; j < _characters.size() ; j++){
+ if(lastspan != _characters[j].in_span){
+ lastspan = _characters[j].in_span;
+ icc = _spans[lastspan].input_stream_first_character;
+ }
+ snprintf(line, sizeof(line), "char %4u: '%c' 0x%4.4x x=%8.4f glyph=%3d span=%3d\n", j, *icc, *icc, _characters[j].x, _characters[j].in_glyph, _characters[j].in_span);
+ result += line;
+ ++icc;
+ }
+ }
+ if(_glyphs.size()){
+ for(unsigned j = 0; j < _glyphs.size() ; j++){
+ snprintf(line, sizeof(line), "glyph %4u: %4d (%8.4f,%8.4f) rot=%8.4f cx=%8.4f char=%4d\n",
+ j, _glyphs[j].glyph, _glyphs[j].x, _glyphs[j].y, _glyphs[j].rotation, _glyphs[j].width, _glyphs[j].in_character);
+ result += line;
+ }
+ }
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
- char line[256];
- snprintf(line, sizeof(line), "==== span %d\n", span_index);
- result += line;
- snprintf(line, sizeof(line), " in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
- direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
- result += line;
- snprintf(line, sizeof(line), " in source %d (type=%d, cookie=%p)\n", _spans[span_index].in_input_stream_item,
+ result += Glib::ustring::compose("==== span %1 \n", span_index)
+ + Glib::ustring::compose(" in para %1 (direction=%2)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
+ direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction))
+ + Glib::ustring::compose(" in source %1 (type=%2, cookie=%3)\n", _spans[span_index].in_input_stream_item,
_input_stream[_spans[span_index].in_input_stream_item]->Type(),
- _input_stream[_spans[span_index].in_input_stream_item]->source_cookie);
- result += line;
- snprintf(line, sizeof(line), " in line %d (baseline=%f, shape=%d)\n", _chunks[_spans[span_index].in_chunk].in_line,
+ _input_stream[_spans[span_index].in_input_stream_item]->source_cookie)
+ + Glib::ustring::compose(" in line %1 (baseline=%2, shape=%3)\n", _chunks[_spans[span_index].in_chunk].in_line,
_lines[_chunks[_spans[span_index].in_chunk].in_line].baseline_y,
- _lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape);
- result += line;
- 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;
+ _lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape)
+ + Glib::ustring::compose(" in chunk %1 (x=%2, baselineshift=%3)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift);
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)));
- result += line;
+ result += Glib::ustring::compose(" font '%1' %2 %3 %4\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)));
}
- snprintf(line, sizeof(line), " x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end);
- result += line;
- snprintf(line, sizeof(line), " line height: ascent %f, descent %f leading %f\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading);
- result += line;
- snprintf(line, sizeof(line), " direction %s, block-progression %s\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression));
- result += line;
- result += " ** characters:\n";
+ result += Glib::ustring::compose(" x_start = %1, x_end = %2\n", _spans[span_index].x_start, _spans[span_index].x_end)
+ + Glib::ustring::compose(" line height: ascent %1, descent %2 leading %3\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading)
+ + Glib::ustring::compose(" direction %1, block-progression %2\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression))
+ + " ** characters:\n";
Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
// very inefficent code. what the hell, it's only debug stuff.
for (unsigned char_index = 0 ; char_index < _characters.size() ; char_index++) {
+ union {const PangoLogAttr* pattr; const unsigned* uattr;} u;
+ u.pattr = &_characters[char_index].char_attributes;
if (_characters[char_index].in_span != span_index) continue;
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) {
- snprintf(line, sizeof(line), " %d: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *(unsigned*) &_characters[char_index].char_attributes, _characters[char_index].in_glyph);
- } else {
- snprintf(line, sizeof(line), " %d: '%c' x=%f flags=%03x glyph=%d\n", char_index, *iter_char, _characters[char_index].x, *(unsigned*) &_characters[char_index].char_attributes, _characters[char_index].in_glyph);
- iter_char++;
+ snprintf(line, sizeof(line), " %u: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
+ } else { // some text has empty tspans, iter_char cannot be dereferenced
+ snprintf(line, sizeof(line), " %u: '%c' 0x%4.4x x=%f flags=%03x glyph=%d\n", char_index, *iter_char, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
+ ++iter_char;
}
result += line;
}
result += " ** glyphs:\n";
for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
if (_characters[_glyphs[glyph_index].in_character].in_span != span_index) continue;
- snprintf(line, sizeof(line), " %d: %d (%f,%f) rot=%f cx=%f char=%d\n", glyph_index, _glyphs[glyph_index].glyph, _glyphs[glyph_index].x, _glyphs[glyph_index].y, _glyphs[glyph_index].rotation, _glyphs[glyph_index].width, _glyphs[glyph_index].in_character);
+ snprintf(line, sizeof(line), " %u: %d (%f,%f) rot=%f cx=%f char=%d\n", glyph_index, _glyphs[glyph_index].glyph, _glyphs[glyph_index].x, _glyphs[glyph_index].y, _glyphs[glyph_index].rotation, _glyphs[glyph_index].width, _glyphs[glyph_index].in_character);
result += line;
}
result += "\n";
diff --git a/src/libnrtype/Layout-TNG.cpp b/src/libnrtype/Layout-TNG.cpp
index bda0d1697..dd9c90949 100644
--- a/src/libnrtype/Layout-TNG.cpp
+++ b/src/libnrtype/Layout-TNG.cpp
@@ -16,9 +16,10 @@ namespace Text {
const gunichar Layout::UNICODE_SOFT_HYPHEN = 0x00AD;
const double Layout::LINE_HEIGHT_NORMAL = 1.25;
-Layout::Layout()
+Layout::Layout() :
+ _input_truncated(0),
+ _path_fitted(NULL)
{
- _path_fitted = NULL;
}
Layout::~Layout()
diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h
index 4406d9f93..c3ccbffb5 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;
@@ -353,6 +353,9 @@ public:
void showGlyphs(CairoRenderContext *ctx) const;
#endif
+ /** Returns the font family of the indexed span */
+ Glib::ustring getFontFamily(unsigned span_index) const;
+
/** debug and unit test method. Creates a textual representation of the
contents of this object. The output is designed to be both human-readable
and comprehensible when diffed with a known-good dump. */
@@ -429,16 +432,17 @@ public:
iterator getLetterAt(double x, double y) const;
inline iterator getLetterAt(Geom::Point &point) const;
- /** Returns an iterator pointing to the character in the output which
+ /* Returns an iterator pointing to the character in the output which
was created from the given input. If the character at the given byte
offset was removed (soft hyphens, for example) the next character after
it is returned. If no input was added with the given cookie, end() is
returned. If more than one input has the same cookie, the first will
be used regardless of the value of \a text_iterator. If
\a text_iterator is out of bounds, the first or last character belonging
- to the given input will be returned accordingly. */
+ to the given input will be returned accordingly.
iterator sourceToIterator(void *source_cookie, Glib::ustring::const_iterator text_iterator) const;
-
+ */
+
/** Returns an iterator pointing to the first character in the output
which was created from the given source. If \a source_cookie is invalid,
end() is returned. If more than one input has the same cookie, the
@@ -565,6 +569,9 @@ public:
inline void setZero() {ascent = descent = leading = 0.0;}
inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;}
void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other
+ inline double getAscent() const {return ascent; }
+ inline double getDescent() const {return descent; }
+ inline double getLeading() const {return leading; }
};
/// see _enum_converter()
@@ -714,6 +721,7 @@ private:
float font_size;
float x_start; /// relative to the start of the chunk
float x_end; /// relative to the start of the chunk
+ inline float width() const {return std::abs(x_start - x_end);}
LineHeight line_height;
double baseline_shift; /// relative to the line's baseline
Direction direction; /// See CSS3 section 3.2. Either rtl or ltr
@@ -810,7 +818,12 @@ class Layout::iterator {
public:
friend class Layout;
// this is just so you can create uninitialised iterators - don't actually try to use one
- iterator() : _parent_layout(NULL) {}
+ iterator() :
+ _parent_layout(NULL),
+ _glyph_index(-1),
+ _char_index(0),
+ _cursor_moving_vertically(false),
+ _x_coordinate(0.0){}
// no copy constructor required, the default does what we want
bool operator== (iterator const &other) const
{return _glyph_index == other._glyph_index && _char_index == other._char_index;}
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/TextWrapper.cpp b/src/libnrtype/TextWrapper.cpp
index 63af17f2e..380e9ba3f 100644
--- a/src/libnrtype/TextWrapper.cpp
+++ b/src/libnrtype/TextWrapper.cpp
@@ -107,8 +107,24 @@ void text_wrapper::AppendUTF8(char const *text, int len)
/* effic: (Not an issue for the sole caller at the time of writing.) This implementation
takes quadratic time if the text is composed of n appends. Use a proper data structure.
STL vector would suffice. */
- utf8_text = (char*)realloc(utf8_text, (utf8_length + nlen + 1) * sizeof(char));
- uni32_codepoint = (int*)realloc(uni32_codepoint, (utf8_length + nlen + 1) * sizeof(int));
+ char *newdata = static_cast<char*>(realloc(utf8_text, (utf8_length + nlen + 1) * sizeof(char)));
+ if (newdata != NULL)
+ {
+ utf8_text = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate utf8_text");
+ }
+ int* newdata2 = static_cast<int*>(realloc(uni32_codepoint, (utf8_length + nlen + 1) * sizeof(int)));
+ if (newdata2 != NULL)
+ {
+ uni32_codepoint = newdata2;
+ }
+ else
+ {
+ g_warning("Failed to reallocate uni32_codepoint");
+ }
// copy the source text in the newly lengthened array
memcpy(utf8_text + utf8_length, text, nlen * sizeof(char));
@@ -164,11 +180,27 @@ void text_wrapper::AppendUTF8(char const *text, int len)
// so setting the dx to 0 is mandatory
if ( uni32_length > last_addition ) {
if ( kern_x ) {
- kern_x = (double*)realloc(kern_x, (uni32_length + 1) * sizeof(double));
+ double *newdata = static_cast<double*>(realloc(kern_x, (uni32_length + 1) * sizeof(double)));
+ if (newdata != NULL)
+ {
+ kern_x = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate kern_x");
+ }
for (int i = last_addition; i <= uni32_length; i++) kern_x[i] = 0;
}
if ( kern_y ) {
- kern_y = (double*)realloc(kern_y, (uni32_length + 1) * sizeof(double));
+ double *newdata = static_cast<double*>(realloc(kern_y, (uni32_length + 1) * sizeof(double)));
+ if (newdata != NULL)
+ {
+ kern_y = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate kern_y");
+ }
for (int i = last_addition; i <= uni32_length; i++) kern_y[i] = 0;
}
}
@@ -214,7 +246,15 @@ void text_wrapper::DoLayout(void)
// realloc the structures
if ( glyph_length >= max_g ) {
max_g = 2 * glyph_length + 1;
- glyph_text = (one_glyph*)realloc(glyph_text, (max_g + 1) * sizeof(one_glyph));
+ one_glyph *newdata = static_cast<one_glyph*>(realloc(glyph_text, (max_g + 1) * sizeof(one_glyph)));
+ if (newdata != NULL)
+ {
+ glyph_text = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate glyph_text");
+ }
}
// fill the glyph info
glyph_text[glyph_length].font = pRun->item->analysis.font;
@@ -287,7 +327,7 @@ void text_wrapper::DoLayout(void)
for (int i = 0; i < glyph_length; i++) {
glyph_text[i].uni_st = uni32_codepoint[glyph_text[i].uni_st];
glyph_text[i].uni_en = uni32_codepoint[glyph_text[i].uni_en];
- glyph_text[i].x /= 512; // why is this not default_font->daddy->fontsize?
+ glyph_text[i].x /= 512; // why is this not default_font->parent->fontsize?
glyph_text[i].y /= 512;
}
if ( glyph_length > 0 ) {
@@ -354,7 +394,15 @@ void text_wrapper::ChunkText(void)
} while ( n_en < g_en && glyph_text[n_en].font == curPF );
if ( nbBox >= maxBox ) {
maxBox = 2 * nbBox + 1;
- boxes = (one_box*)realloc(boxes, maxBox * sizeof(one_box));
+ one_box *newdata = static_cast<one_box*>(realloc(boxes, maxBox * sizeof(one_box)));
+ if (newdata != NULL)
+ {
+ boxes = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate boxes");
+ }
}
boxes[nbBox].g_st = n_st;
boxes[nbBox].g_en = n_en;
@@ -378,7 +426,15 @@ void text_wrapper::ChunkText(void)
if ( b_en < nbBox && boxes[b_en].g_en == g_en ) {
if ( nbPara >= maxPara ) {
maxPara = 2 * nbPara + 1;
- paras = (one_para*)realloc(paras, maxPara * sizeof(one_para));
+ one_para *newdata = static_cast<one_para*>(realloc(paras, maxPara * sizeof(one_para)));
+ if (newdata != NULL)
+ {
+ paras = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate paras");
+ }
}
paras[nbPara].b_st = b_st;
paras[nbPara].b_en = b_en;
@@ -571,7 +627,15 @@ unsigned text_wrapper::AddBoundary(text_boundary const &ib)
{
if ( nbBound >= maxBound ) {
maxBound = 2 * nbBound + 1;
- bounds = (text_boundary*)realloc(bounds, maxBound * sizeof(text_boundary));
+ text_boundary *newdata = static_cast<text_boundary*>(realloc(bounds, maxBound * sizeof(text_boundary)));
+ if (newdata != NULL)
+ {
+ bounds = newdata;
+ }
+ else
+ {
+ g_warning("Failed to reallocate bounds");
+ }
}
unsigned const ix = nbBound++;
bounds[ix] = ib;
@@ -893,7 +957,7 @@ void text_wrapper::AddDxDy(void)
} else if ( l_pos > n_pos ) {
for (int j = l_pos; j > n_pos; j--) sum -= kern_x[j];
}
- l_pos = n_pos;
+ // l_pos = n_pos;
glyph_text[glyph_length].x += sum;
}
}
@@ -918,7 +982,7 @@ void text_wrapper::AddDxDy(void)
} else if ( l_pos > n_pos ) {
for (int j = l_pos; j > n_pos; j--) sum -= kern_y[j];
}
- l_pos = n_pos;
+ // l_pos = n_pos;
glyph_text[glyph_length].y += sum;
}
}
diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h
index d00569984..75106617b 100644
--- a/src/libnrtype/font-instance.h
+++ b/src/libnrtype/font-instance.h
@@ -29,7 +29,7 @@ public:
// refcount
int refCount;
// font_factory owning this font_instance
- font_factory* daddy;
+ font_factory* parent;
// common glyph definitions for all the rasterfonts
std::map<int, int> id_to_no;
@@ -59,6 +59,8 @@ public:
double Advance(int glyph_id, bool vertical);
// nominal advance of the font.
bool FontMetrics(double &ascent, double &descent, double &leading);
+ bool FontDecoration(double &underline_position, double &underline_thickness,
+ double &linethrough_position, double &linethrough_thickness);
bool FontSlope(double &run, double &rise);
// for generating slanted cursors for oblique fonts
Geom::OptRect BBox(int glyph_id);
diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp
index 710e0b84f..89d0cb037 100644
--- a/src/libnrtype/font-lister.cpp
+++ b/src/libnrtype/font-lister.cpp
@@ -2,32 +2,44 @@
# 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();
iter != familyStyleMap.end();
- iter++) {
+ ++iter) {
familyList.push_back((*iter).first);
}
familyList.sort();
@@ -40,22 +52,914 @@ 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;
std::list<Glib::ustring> &styleStrings = familyStyleMap[familyName];
for (std::list<Glib::ustring>::iterator it=styleStrings.begin();
it != styleStrings.end();
- it++) {
+ ++it) {
styles = g_list_append(styles, g_strdup((*it).c_str()));
}
(*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.empty() && !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();
+ }
+
+ // FIXME: why do we parse the style attribute instead of the object's SPStyle?
+ 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);
+ if(!family)
+ family = "sans-serif";
+ 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 +971,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);